1 /*****************************************************************************
2  *
3  * MACROS.C - Common macro functions for Nagios
4  *
5  *
6  * License:
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  *
21  *****************************************************************************/
22 
23 #include "../include/config.h"
24 #include "../include/macros.h"
25 #include "../include/common.h"
26 #include "../include/objects.h"
27 #include "../include/statusdata.h"
28 #include "../include/comments.h"
29 #ifdef NSCORE
30 #include "../include/nagios.h"
31 #else
32 #include "../include/cgiutils.h"
33 #endif
34 
35 static char *macro_x_names[MACRO_X_COUNT]; /* the macro names */
36 char *macro_user[MAX_USER_MACROS]; /* $USERx$ macros */
37 
38 struct macro_key_code {
39 	char *name;  /* macro key name */
40 	char *value; /* macro value */
41 	int code;  /* numeric macro code, usable in case statements */
42 	int options; /* Options for how the macro can be escaped */
43 };
44 
45 static struct macro_key_code macro_keys[MACRO_X_COUNT];
46 
47 /*
48  * These point to their corresponding pointer arrays in global_macros
49  * AFTER macros have been initialized.
50  *
51  * They really only exist so that eventbroker modules that reference
52  * them won't need to be re-compiled, although modules that rely
53  * on their values after having run a certain command will require an
54  * update
55  */
56 static char **macro_x = NULL;
57 
58 /*
59  * scoped to this file to prevent (unintentional) mischief,
60  * but see base/notifications.c for how to use it
61  */
62 static nagios_macros global_macros;
63 
64 
65 nagios_macros *get_global_macros(void) {
66 	return &global_macros;
67 	}
68 
69 /******************************************************************/
70 /************************ MACRO FUNCTIONS *************************/
71 /******************************************************************/
72 
73 /*
74  * locate a macro key based on its name by using a binary search
75  * over all keys. O(log(n)) complexity and a vast improvement over
76  * the previous linear scan
77  */
78 static const struct macro_key_code *find_macro_key(const char *name) {
79 	unsigned int high, low = 0;
80 	int value;
81 	struct macro_key_code *key;
82 
83 	high = MACRO_X_COUNT;
84 	while (high - low > 0) {
85 		unsigned int mid = low + ((high - low) / 2);
86 		key = &macro_keys[mid];
87 		value = strcmp(name, key->name);
88 		if (value == 0) {
89 			return key;
90 			}
91 		if (value > 0)
92 			low = mid + 1;
93 		else
94 			high = mid;
95 		}
96 	return NULL;
97 	}
98 
99 
100 /*
101  * replace macros in notification commands with their values,
102  * the thread-safe version
103  */
104 int process_macros_r(nagios_macros *mac, char *input_buffer, char **output_buffer, int options) {
105 	char *temp_buffer = NULL;
106 	char *save_buffer = NULL;
107 	char *buf_ptr = NULL;
108 	char *delim_ptr = NULL;
109 	int in_macro = FALSE;
110 	char *selected_macro = NULL;
111 	char *original_macro = NULL;
112 	int result = OK;
113 	int free_macro = FALSE;
114 	int macro_options = 0;
115 
116 	log_debug_info(DEBUGL_FUNCTIONS, 0, "process_macros_r()\n");
117 
118 	if(output_buffer == NULL) {
119 		return ERROR;
120 	}
121 
122 	*output_buffer = (char *)strdup("");
123 
124 	if(input_buffer == NULL) {
125 		return ERROR;
126 	}
127 
128 	in_macro = FALSE;
129 
130 	log_debug_info(DEBUGL_MACROS, 1, "**** BEGIN MACRO PROCESSING ***********\n");
131 	log_debug_info(DEBUGL_MACROS, 1, "Processing: '%s'\n", input_buffer);
132 
133 	/* use a duplicate of original buffer, so we don't modify the original */
134 	save_buffer = buf_ptr = (input_buffer ? strdup(input_buffer) : NULL);
135 
136 	while(buf_ptr) {
137 
138 		/* save pointer to this working part of buffer */
139 		temp_buffer = buf_ptr;
140 
141 		/* find the next delimiter - terminate preceding string and advance buffer pointer for next run */
142 		delim_ptr = strchr(buf_ptr, '$');
143 		if(delim_ptr != NULL) {
144 			delim_ptr[0] = '\x0';
145 			buf_ptr = (char *)delim_ptr + 1;
146 		}
147 		/* no delimiter found - we already have the last of the buffer */
148 		else {
149 			buf_ptr = NULL;
150 		}
151 
152 		log_debug_info(DEBUGL_MACROS, 2, "  Processing part: '%s'\n", temp_buffer);
153 
154 		selected_macro = NULL;
155 
156 		/* we're in plain text... */
157 		if(in_macro == FALSE) {
158 
159 			/* add the plain text to the end of the already processed buffer */
160 			*output_buffer = (char *)realloc(*output_buffer, strlen(*output_buffer) + strlen(temp_buffer) + 1);
161 			strcat(*output_buffer, temp_buffer);
162 
163 			log_debug_info(DEBUGL_MACROS, 2, "  Not currently in macro.  Running output (%lu): '%s'\n", (unsigned long)strlen(*output_buffer), *output_buffer);
164 			in_macro = TRUE;
165 		}
166 
167 		/* looks like we're in a macro, so process it... */
168 		else {
169 
170 			/* by default, we only free when instructed */
171 			free_macro = FALSE;
172 
173 			/* grab the macro value */
174 			result = grab_macro_value_r(mac, temp_buffer, &selected_macro, &macro_options, &free_macro);
175 			log_debug_info(DEBUGL_MACROS, 2, "  Processed '%s', Free: %d\n", temp_buffer, free_macro);
176 
177 			/* an error occurred - we couldn't parse the macro, so continue on */
178 			if (result != OK) {
179 
180 				log_debug_info(DEBUGL_MACROS, 0, " WARNING: An error occurred processing macro '%s'!\n", temp_buffer);
181 				if(free_macro == TRUE) {
182 					my_free(selected_macro);
183 				}
184 
185 				/* an escaped $ is done by specifying two $$ next to each other */
186 				if(!strcmp(temp_buffer, "")) {
187 					log_debug_info(DEBUGL_MACROS, 2, "  Escaped $.  Running output (%lu): '%s'\n", (unsigned long)strlen(*output_buffer), *output_buffer);
188 					*output_buffer = (char *)realloc(*output_buffer, strlen(*output_buffer) + 2);
189 					strcat(*output_buffer, "$");
190 				}
191 
192 				/* a non-macro, just some user-defined string between two $s */
193 				else {
194 					log_debug_info(DEBUGL_MACROS, 2, "  Non-macro.  Running output (%lu): '%s'\n", (unsigned long)strlen(*output_buffer), *output_buffer);
195 
196 					/* add the plain text to the end of the already processed buffer */
197 					*output_buffer = (char *)realloc(*output_buffer, strlen(*output_buffer) + strlen(temp_buffer) + 3);
198 					strcat(*output_buffer, "$");
199 					strcat(*output_buffer, temp_buffer);
200 
201 					/* just could have been a stray $ */
202 					if (buf_ptr != NULL) {
203 						strcat(*output_buffer, "$");
204 					}
205 				}
206 			}
207 
208 			/* insert macro */
209 			if (selected_macro != NULL) {
210 
211 				log_debug_info(DEBUGL_MACROS, 2, "  Processed '%s', Free: %d,  Cleaning options: %d\n", temp_buffer, free_macro, options);
212 
213 				/* URL encode the macro if requested - this allocates new memory */
214 				if(options & URL_ENCODE_MACRO_CHARS) {
215 					original_macro = selected_macro;
216 					selected_macro = get_url_encoded_string(selected_macro);
217 					if(free_macro == TRUE) {
218 						my_free(original_macro);
219 					}
220 					free_macro = TRUE;
221 				}
222 
223 				/* some macros should sometimes be cleaned */
224 				if(macro_options & options & (STRIP_ILLEGAL_MACRO_CHARS | ESCAPE_MACRO_CHARS)) {
225 
226 					char *cleaned_macro = NULL;
227 
228 					/* add the (cleaned) processed macro to the end of the already processed buffer */
229 					if(selected_macro != NULL && (cleaned_macro = clean_macro_chars(selected_macro, options)) != NULL) {
230 						*output_buffer = (char *)realloc(*output_buffer, strlen(*output_buffer) + strlen(cleaned_macro) + 1);
231 						strcat(*output_buffer, cleaned_macro);
232 						if(*cleaned_macro) {
233 							my_free(cleaned_macro);
234 						}
235 
236 						log_debug_info(DEBUGL_MACROS, 2, "  Cleaned macro.  Running output (%lu): '%s'\n", (unsigned long)strlen(*output_buffer), *output_buffer);
237 					}
238 				}
239 
240 				/* others are not cleaned */
241 				else {
242 					/* add the processed macro to the end of the already processed buffer */
243 					if(selected_macro != NULL) {
244 						*output_buffer = (char *)realloc(*output_buffer, strlen(*output_buffer) + strlen(selected_macro) + 1);
245 						strcat(*output_buffer, selected_macro);
246 
247 						log_debug_info(DEBUGL_MACROS, 2, "  Uncleaned macro.  Running output (%lu): '%s'\n", (unsigned long)strlen(*output_buffer), *output_buffer);
248 					}
249 				}
250 
251 				/* free memory if necessary (if we URL encoded the macro or we were told to do so by grab_macro_value()) */
252 				if(free_macro == TRUE) {
253 					my_free(selected_macro);
254 				}
255 
256 				log_debug_info(DEBUGL_MACROS, 2, "  Just finished macro.  Running output (%lu): '%s'\n", (unsigned long)strlen(*output_buffer), *output_buffer);
257 			}
258 
259 			in_macro = FALSE;
260 		} /* if(in_macro == TRUE) */
261 	} /* while(buf_ptr) */
262 
263 	/* free copy of input buffer */
264 	my_free(save_buffer);
265 
266 	log_debug_info(DEBUGL_MACROS, 1, "  Done.  Final output: '%s'\n", *output_buffer);
267 	log_debug_info(DEBUGL_MACROS, 1, "**** END MACRO PROCESSING *************\n");
268 
269 	return OK;
270 }
271 
272 int process_macros(char *input_buffer, char **output_buffer, int options) {
273 	return process_macros_r(&global_macros, input_buffer, output_buffer, options);
274 	}
275 
276 /******************************************************************/
277 /********************** MACRO GRAB FUNCTIONS **********************/
278 /******************************************************************/
279 
280 /* grab macros that are specific to a particular host */
281 int grab_host_macros_r(nagios_macros *mac, host *hst) {
282 	/* clear host-related macros */
283 	clear_host_macros_r(mac);
284 	clear_hostgroup_macros_r(mac);
285 
286 	/* save pointer to host */
287 	mac->host_ptr = hst;
288 	mac->hostgroup_ptr = NULL;
289 
290 	if(hst == NULL)
291 		return ERROR;
292 
293 	/* save pointer to host's first/primary hostgroup */
294 	if(hst->hostgroups_ptr)
295 		mac->hostgroup_ptr = (hostgroup *)hst->hostgroups_ptr->object_ptr;
296 
297 	return OK;
298 	}
299 
300 int grab_host_macros(host *hst) {
301 	return grab_host_macros_r(&global_macros, hst);
302 	}
303 
304 
305 /* grab hostgroup macros */
306 int grab_hostgroup_macros_r(nagios_macros *mac, hostgroup *hg) {
307 	/* clear hostgroup macros */
308 	clear_hostgroup_macros_r(mac);
309 
310 	/* save the hostgroup pointer for later */
311 	mac->hostgroup_ptr = hg;
312 
313 	if(hg == NULL)
314 		return ERROR;
315 
316 	return OK;
317 	}
318 
319 int grab_hostgroup_macros(hostgroup *hg) {
320 	return grab_hostgroup_macros_r(&global_macros, hg);
321 	}
322 
323 
324 /* grab macros that are specific to a particular service */
325 int grab_service_macros_r(nagios_macros *mac, service *svc) {
326 
327 	/* clear service-related macros */
328 	clear_service_macros_r(mac);
329 	clear_servicegroup_macros_r(mac);
330 
331 	/* save pointer for later */
332 	mac->service_ptr = svc;
333 	mac->servicegroup_ptr = NULL;
334 
335 	if(svc == NULL)
336 		return ERROR;
337 
338 	/* save first/primary servicegroup pointer for later */
339 	if(svc->servicegroups_ptr)
340 		mac->servicegroup_ptr = (servicegroup *)svc->servicegroups_ptr->object_ptr;
341 
342 	return OK;
343 	}
344 
345 int grab_service_macros(service *svc) {
346 	return grab_service_macros_r(&global_macros, svc);
347 	}
348 
349 
350 /* grab macros that are specific to a particular servicegroup */
351 int grab_servicegroup_macros_r(nagios_macros *mac, servicegroup *sg) {
352 	/* clear servicegroup macros */
353 	clear_servicegroup_macros_r(mac);
354 
355 	/* save the pointer for later */
356 	mac->servicegroup_ptr = sg;
357 
358 	if(sg == NULL)
359 		return ERROR;
360 
361 	return OK;
362 	}
363 
364 int grab_servicegroup_macros(servicegroup *sg) {
365 	return grab_servicegroup_macros_r(&global_macros, sg);
366 	}
367 
368 
369 /* grab macros that are specific to a particular contact */
370 int grab_contact_macros_r(nagios_macros *mac, contact *cntct) {
371 	/* clear contact-related macros */
372 	clear_contact_macros_r(mac);
373 	clear_contactgroup_macros_r(mac);
374 
375 	/* save pointer to contact for later */
376 	mac->contact_ptr = cntct;
377 	mac->contactgroup_ptr = NULL;
378 
379 	if(cntct == NULL)
380 		return ERROR;
381 
382 	/* save pointer to first/primary contactgroup for later */
383 	if(cntct->contactgroups_ptr)
384 		mac->contactgroup_ptr = (contactgroup *)cntct->contactgroups_ptr->object_ptr;
385 
386 	return OK;
387 	}
388 
389 int grab_contact_macros(contact *cntct) {
390 	return grab_contact_macros_r(&global_macros, cntct);
391 	}
392 
393 int grab_argv_macros_r(nagios_macros *mac, char *check_command) {
394 	char *cmd_ptr = check_command;
395 	char temp_arg[MAX_COMMAND_BUFFER] = "";
396 	char *arg_buffer = NULL;
397 	int macro_options = STRIP_ILLEGAL_MACRO_CHARS | ESCAPE_MACRO_CHARS;
398 	register int x = 0;
399 	register int y = 0;
400 	register int arg_index = 0;
401 
402 	/* clear the argv macros */
403 	clear_argv_macros_r(mac);
404 
405 	/* make sure we've got all the requirements */
406 	if(cmd_ptr == NULL || cmd_ptr[0] == '\0')
407 		return ERROR;
408 
409 	/* skip the command name (we're about to get the arguments)... */
410 	for(arg_index = 0;; arg_index++) {
411 		if(cmd_ptr[arg_index] == '!' || cmd_ptr[arg_index] == '\x0')
412 			break;
413 		}
414 
415 	/* get each command argument */
416 	for(x = 0; x < MAX_COMMAND_ARGUMENTS; x++) {
417 		/* we reached the end of the arguments... */
418 		if(cmd_ptr[arg_index] == '\x0')
419 			break;
420 
421 		/* get the next argument */
422 		/* can't use strtok(), as that's used in process_macros... */
423 		for(arg_index++, y = 0; y < (int)sizeof(temp_arg) - 1; arg_index++) {
424 
425 			/* handle escaped argument delimiters */
426 			if(cmd_ptr[arg_index] == '\\' && cmd_ptr[arg_index+1] == '!') {
427 				arg_index++;
428 			} else if(cmd_ptr[arg_index] == '!' || cmd_ptr[arg_index] == '\x0') {
429 				/* end of argument */
430 				break;
431 			}
432 
433 			/* copy the character */
434 			temp_arg[y] = cmd_ptr[arg_index];
435 			y++;
436 			}
437 		temp_arg[y] = '\x0';
438 
439 		process_macros_r(mac, temp_arg, &arg_buffer, macro_options);
440 
441 		mac->argv[x] = arg_buffer;
442 		}
443 
444 	return OK;
445 
446 	}
447 
448 /******************************************************************/
449 /******************* MACRO GENERATION FUNCTIONS *******************/
450 /******************************************************************/
451 
452 /* this is the big one */
453 int grab_macro_value_r(nagios_macros *mac, char *macro_buffer, char **output, int *clean_options, int *free_macro) {
454 	char *buf = NULL;
455 	char *ptr = NULL;
456 	char *macro_name = NULL;
457 	char *arg[2] = {NULL, NULL};
458 	contact *temp_contact = NULL;
459 	contactgroup *temp_contactgroup = NULL;
460 	contactsmember *temp_contactsmember = NULL;
461 	char *temp_buffer = NULL;
462 	int delimiter_len = 0;
463 	int x, result = OK;
464 	const struct macro_key_code *mkey;
465 
466 	/* for the early cases, this is the default */
467 	*free_macro = FALSE;
468 
469 	if(output == NULL)
470 		return ERROR;
471 
472 	/* clear the old macro value */
473 	my_free(*output);
474 
475 	if(macro_buffer == NULL || free_macro == NULL)
476 		return ERROR;
477 
478 	if(clean_options)
479 		*clean_options = 0;
480 
481 	/*
482 	 * We handle argv and user macros first, since those are by far
483 	 * the most commonly accessed ones (3.4 and 1.005 per check,
484 	 * respectively). Since neither of them requires that we copy
485 	 * the original buffer, we can also get away with some less
486 	 * code for these simple cases.
487 	 */
488 	if(strstr(macro_buffer, "ARG") == macro_buffer) {
489 
490 		/* which arg do we want? */
491 		x = atoi(macro_buffer + 3);
492 
493 		if(x <= 0 || x > MAX_COMMAND_ARGUMENTS) {
494 			return ERROR;
495 			}
496 
497 		/* use a pre-computed macro value */
498 		*output = mac->argv[x - 1];
499 		return OK;
500 		}
501 
502 	if(strstr(macro_buffer, "USER") == macro_buffer) {
503 
504 		/* which macro do we want? */
505 		x = atoi(macro_buffer + 4);
506 
507 		if(x <= 0 || x > MAX_USER_MACROS) {
508 			return ERROR;
509 			}
510 
511 		/* use a pre-computed macro value */
512 		*output = macro_user[x - 1];
513 		return OK;
514 		}
515 
516 	/* most frequently used "x" macro gets a shortcut */
517 	if(mac->host_ptr && !strcmp(macro_buffer, "HOSTADDRESS")) {
518 		if(mac->host_ptr->address)
519 			*output = mac->host_ptr->address;
520 		return OK;
521 		}
522 
523 	/* work with a copy of the original buffer */
524 	if((buf = (char *)strdup(macro_buffer)) == NULL)
525 		return ERROR;
526 
527 	/* macro name is at start of buffer */
528 	macro_name = buf;
529 
530 	/* see if there's an argument - if so, this is most likely an on-demand macro */
531 	if((ptr = strchr(buf, ':'))) {
532 
533 		ptr[0] = '\x0';
534 		ptr++;
535 
536 		/* save the first argument - host name, hostgroup name, etc. */
537 		arg[0] = ptr;
538 
539 		/* try and find a second argument */
540 		if((ptr = strchr(ptr, ':'))) {
541 
542 			ptr[0] = '\x0';
543 			ptr++;
544 
545 			/* save second argument - service description or delimiter */
546 			arg[1] = ptr;
547 			}
548 		}
549 
550 	if ((mkey = find_macro_key(macro_name))) {
551 		log_debug_info(DEBUGL_MACROS, 2, "  macros[%d] (%s) match.\n", mkey->code, macro_x_names[mkey->code]);
552 
553 		/* get the macro value */
554 		result = grab_macrox_value_r(mac, mkey->code, arg[0], arg[1], output, free_macro);
555 
556 		/* Return the macro attributes */
557 
558 		if(clean_options) {
559 			*clean_options = mkey->options;
560 			}
561 		}
562 	/***** CONTACT ADDRESS MACROS *****/
563 	/* NOTE: the code below should be broken out into a separate function */
564 	else if(strstr(macro_name, "CONTACTADDRESS") == macro_name) {
565 
566 		/* which address do we want? */
567 		x = atoi(macro_name + 14) - 1;
568 
569 		/* regular macro */
570 		if(arg[0] == NULL) {
571 
572 			/* use the saved pointer */
573 			if((temp_contact = mac->contact_ptr) == NULL) {
574 				my_free(buf);
575 				return ERROR;
576 				}
577 
578 			/* get the macro value by reference, so no need to free() */
579 			*free_macro = FALSE;
580 			result = grab_contact_address_macro(x, temp_contact, output);
581 			}
582 
583 		/* on-demand macro */
584 		else {
585 
586 			/* on-demand contact macro with a contactgroup and a delimiter */
587 			if(arg[1] != NULL) {
588 
589 				if((temp_contactgroup = find_contactgroup(arg[0])) == NULL)
590 					return ERROR;
591 
592 				delimiter_len = strlen(arg[1]);
593 
594 				/* concatenate macro values for all contactgroup members */
595 				for(temp_contactsmember = temp_contactgroup->members; temp_contactsmember != NULL; temp_contactsmember = temp_contactsmember->next) {
596 
597 					if((temp_contact = temp_contactsmember->contact_ptr) == NULL)
598 						continue;
599 
600 					/* get the macro value for this contact */
601 					grab_contact_address_macro(x, temp_contact, &temp_buffer);
602 
603 					if(temp_buffer == NULL)
604 						continue;
605 
606 					/* add macro value to already running macro */
607 					if(*output == NULL)
608 						*output = (char *)strdup(temp_buffer);
609 					else {
610 						if((*output = (char *)realloc(*output, strlen(*output) + strlen(temp_buffer) + delimiter_len + 1)) == NULL)
611 							continue;
612 						strcat(*output, arg[1]);
613 						strcat(*output, temp_buffer);
614 						}
615 					my_free(temp_buffer);
616 					}
617 				}
618 
619 			/* else on-demand contact macro */
620 			else {
621 
622 				/* find the contact */
623 				if((temp_contact = find_contact(arg[0])) == NULL) {
624 					my_free(buf);
625 					return ERROR;
626 					}
627 
628 				/* get the macro value */
629 				result = grab_contact_address_macro(x, temp_contact, output);
630 				}
631 			}
632 		}
633 
634 	/***** CUSTOM VARIABLE MACROS *****/
635 	else if(macro_name[0] == '_') {
636 
637 		/* get the macro value */
638 		result = grab_custom_macro_value_r(mac, macro_name, arg[0], arg[1], output);
639 		}
640 
641 	/* no macro matched... */
642 	else {
643 		log_debug_info(DEBUGL_MACROS, 0, " WARNING: Could not find a macro matching '%s'!\n", macro_name);
644 		result = ERROR;
645 		}
646 
647 	/* free memory */
648 	my_free(buf);
649 
650 	return result;
651 	}
652 
653 int grab_macro_value(char *macro_buffer, char **output, int *clean_options, int *free_macro) {
654 	return grab_macro_value_r(&global_macros, macro_buffer, output, clean_options, free_macro);
655 	}
656 
657 
658 int grab_macrox_value_r(nagios_macros *mac, int macro_type, char *arg1, char *arg2, char **output, int *free_macro) {
659 	host *temp_host = NULL;
660 	hostgroup *temp_hostgroup = NULL;
661 	hostsmember *temp_hostsmember = NULL;
662 	service *temp_service = NULL;
663 	servicegroup *temp_servicegroup = NULL;
664 	servicesmember *temp_servicesmember = NULL;
665 	contact *temp_contact = NULL;
666 	contactgroup *temp_contactgroup = NULL;
667 	contactsmember *temp_contactsmember = NULL;
668 	char *temp_buffer = NULL;
669 	int result = OK;
670 	int delimiter_len = 0;
671 	int free_sub_macro = FALSE;
672 #ifdef NSCORE
673 	register int x;
674 	int authorized = TRUE;
675 	int problem = TRUE;
676 	int hosts_up = 0;
677 	int hosts_down = 0;
678 	int hosts_unreachable = 0;
679 	int hosts_down_unhandled = 0;
680 	int hosts_unreachable_unhandled = 0;
681 	int host_problems = 0;
682 	int host_problems_unhandled = 0;
683 	int services_ok = 0;
684 	int services_warning = 0;
685 	int services_unknown = 0;
686 	int services_critical = 0;
687 	int services_warning_unhandled = 0;
688 	int services_unknown_unhandled = 0;
689 	int services_critical_unhandled = 0;
690 	int service_problems = 0;
691 	int service_problems_unhandled = 0;
692 #endif
693 
694 
695 	if(output == NULL || free_macro == NULL)
696 		return ERROR;
697 
698 	*free_macro = FALSE;
699 
700 	/* handle the macro */
701 	switch(macro_type) {
702 
703 			/***************/
704 			/* HOST MACROS */
705 			/***************/
706 		case MACRO_HOSTGROUPNAMES:
707 			*free_macro = TRUE;
708 		case MACRO_HOSTINFOURL:
709 		case MACRO_HOSTNAME:
710 		case MACRO_HOSTALIAS:
711 		case MACRO_HOSTADDRESS:
712 		case MACRO_LASTHOSTCHECK:
713 		case MACRO_LASTHOSTSTATECHANGE:
714 		case MACRO_HOSTOUTPUT:
715 		case MACRO_HOSTPERFDATA:
716 		case MACRO_HOSTSTATE:
717 		case MACRO_HOSTSTATEID:
718 		case MACRO_HOSTATTEMPT:
719 		case MACRO_HOSTEXECUTIONTIME:
720 		case MACRO_HOSTLATENCY:
721 		case MACRO_HOSTDURATION:
722 		case MACRO_HOSTDURATIONSEC:
723 		case MACRO_HOSTDOWNTIME:
724 		case MACRO_HOSTSTATETYPE:
725 		case MACRO_HOSTPERCENTCHANGE:
726 		case MACRO_HOSTACKAUTHOR:
727 		case MACRO_HOSTACKCOMMENT:
728 		case MACRO_LASTHOSTUP:
729 		case MACRO_LASTHOSTDOWN:
730 		case MACRO_LASTHOSTUNREACHABLE:
731 		case MACRO_HOSTCHECKCOMMAND:
732 		case MACRO_HOSTDISPLAYNAME:
733 		case MACRO_HOSTACTIONURL:
734 		case MACRO_HOSTNOTESURL:
735 		case MACRO_HOSTNOTES:
736 		case MACRO_HOSTCHECKTYPE:
737 		case MACRO_LONGHOSTOUTPUT:
738 		case MACRO_HOSTNOTIFICATIONNUMBER:
739 		case MACRO_HOSTNOTIFICATIONID:
740 		case MACRO_HOSTEVENTID:
741 		case MACRO_LASTHOSTEVENTID:
742 		case MACRO_HOSTACKAUTHORNAME:
743 		case MACRO_HOSTACKAUTHORALIAS:
744 		case MACRO_MAXHOSTATTEMPTS:
745 		case MACRO_TOTALHOSTSERVICES:
746 		case MACRO_TOTALHOSTSERVICESOK:
747 		case MACRO_TOTALHOSTSERVICESWARNING:
748 		case MACRO_TOTALHOSTSERVICESUNKNOWN:
749 		case MACRO_TOTALHOSTSERVICESCRITICAL:
750 		case MACRO_HOSTPROBLEMID:
751 		case MACRO_LASTHOSTPROBLEMID:
752 		case MACRO_LASTHOSTSTATE:
753 		case MACRO_LASTHOSTSTATEID:
754 		case MACRO_HOSTIMPORTANCE:
755 		case MACRO_HOSTANDSERVICESIMPORTANCE:
756 		case MACRO_HOSTNOTIFICATIONENABLED:
757 		case MACRO_HOSTNOTIFICATIONPERIOD:
758 
759 			/* a standard host macro */
760 			if(arg2 == NULL) {
761 
762 				/* find the host for on-demand macros */
763 				if(arg1) {
764 					if((temp_host = find_host(arg1)) == NULL) {
765 						if (macro_type == MACRO_HOSTSTATEID) {
766 							*output = strdup("2");
767 							return OK;
768 							}
769 						return ERROR;
770 						}
771 					}
772 
773 				/* else use saved host pointer */
774 				else if((temp_host = mac->host_ptr) == NULL)
775 					return ERROR;
776 
777 				/* get the host macro value */
778 				result = grab_standard_host_macro_r(mac, macro_type, temp_host, output, free_macro);
779 				}
780 
781 			/* a host macro with a hostgroup name and delimiter */
782 			else {
783 
784 				if((temp_hostgroup = find_hostgroup(arg1)) == NULL)
785 					return ERROR;
786 
787 				delimiter_len = strlen(arg2);
788 
789 				/* concatenate macro values for all hostgroup members */
790 				for(temp_hostsmember = temp_hostgroup->members; temp_hostsmember != NULL; temp_hostsmember = temp_hostsmember->next) {
791 
792 					if((temp_host = temp_hostsmember->host_ptr) == NULL)
793 						continue;
794 
795 					/* get the macro value for this host */
796 					grab_standard_host_macro_r(mac, macro_type, temp_host, &temp_buffer, &free_sub_macro);
797 
798 					if(temp_buffer == NULL)
799 						continue;
800 
801 					/* add macro value to already running macro */
802 					if(*output == NULL)
803 						*output = (char *)strdup(temp_buffer);
804 					else {
805 						if((*output = (char *)realloc(*output, strlen(*output) + strlen(temp_buffer) + delimiter_len + 1)) == NULL)
806 							continue;
807 						strcat(*output, arg2);
808 						strcat(*output, temp_buffer);
809 						}
810 					if(free_sub_macro == TRUE)
811 						my_free(temp_buffer);
812 					}
813 				}
814 			break;
815 
816 			/********************/
817 			/* HOSTGROUP MACROS */
818 			/********************/
819 		case MACRO_HOSTGROUPMEMBERS:
820 		case MACRO_HOSTGROUPMEMBERADDRESSES:
821 		case MACRO_HOSTGROUPNOTES:
822 		case MACRO_HOSTGROUPNOTESURL:
823 		case MACRO_HOSTGROUPACTIONURL:
824 			*free_macro = TRUE;
825 		case MACRO_HOSTGROUPNAME:
826 		case MACRO_HOSTGROUPALIAS:
827 
828 			/* a standard hostgroup macro */
829 			/* use the saved hostgroup pointer */
830 			if(arg1 == NULL) {
831 				if((temp_hostgroup = mac->hostgroup_ptr) == NULL)
832 					return ERROR;
833 				}
834 
835 			/* else find the hostgroup for on-demand macros */
836 			else {
837 				if((temp_hostgroup = find_hostgroup(arg1)) == NULL)
838 					return ERROR;
839 				}
840 
841 			/* get the hostgroup macro value */
842 			result = grab_standard_hostgroup_macro_r(mac, macro_type, temp_hostgroup, output);
843 			break;
844 
845 			/******************/
846 			/* SERVICE MACROS */
847 			/******************/
848 		case MACRO_SERVICEGROUPNAMES:
849 			*free_macro = TRUE;
850 		case MACRO_SERVICEINFOURL:
851 		case MACRO_SERVICEDESC:
852 		case MACRO_SERVICESTATE:
853 		case MACRO_SERVICESTATEID:
854 		case MACRO_SERVICEATTEMPT:
855 		case MACRO_LASTSERVICECHECK:
856 		case MACRO_LASTSERVICESTATECHANGE:
857 		case MACRO_SERVICEOUTPUT:
858 		case MACRO_SERVICEPERFDATA:
859 		case MACRO_SERVICEEXECUTIONTIME:
860 		case MACRO_SERVICELATENCY:
861 		case MACRO_SERVICEDURATION:
862 		case MACRO_SERVICEDURATIONSEC:
863 		case MACRO_SERVICEDOWNTIME:
864 		case MACRO_SERVICESTATETYPE:
865 		case MACRO_SERVICEPERCENTCHANGE:
866 		case MACRO_SERVICEACKAUTHOR:
867 		case MACRO_SERVICEACKCOMMENT:
868 		case MACRO_LASTSERVICEOK:
869 		case MACRO_LASTSERVICEWARNING:
870 		case MACRO_LASTSERVICEUNKNOWN:
871 		case MACRO_LASTSERVICECRITICAL:
872 		case MACRO_SERVICECHECKCOMMAND:
873 		case MACRO_SERVICEDISPLAYNAME:
874 		case MACRO_SERVICEACTIONURL:
875 		case MACRO_SERVICENOTESURL:
876 		case MACRO_SERVICENOTES:
877 		case MACRO_SERVICECHECKTYPE:
878 		case MACRO_LONGSERVICEOUTPUT:
879 		case MACRO_SERVICENOTIFICATIONNUMBER:
880 		case MACRO_SERVICENOTIFICATIONID:
881 		case MACRO_SERVICEEVENTID:
882 		case MACRO_LASTSERVICEEVENTID:
883 		case MACRO_SERVICEACKAUTHORNAME:
884 		case MACRO_SERVICEACKAUTHORALIAS:
885 		case MACRO_MAXSERVICEATTEMPTS:
886 		case MACRO_SERVICEISVOLATILE:
887 		case MACRO_SERVICEPROBLEMID:
888 		case MACRO_LASTSERVICEPROBLEMID:
889 		case MACRO_LASTSERVICESTATE:
890 		case MACRO_LASTSERVICESTATEID:
891 		case MACRO_SERVICEIMPORTANCE:
892 		case MACRO_SERVICENOTIFICATIONENABLED:
893 		case MACRO_SERVICENOTIFICATIONPERIOD:
894 
895 			/* use saved service pointer */
896 			if(arg1 == NULL && arg2 == NULL) {
897 
898 				if((temp_service = mac->service_ptr) == NULL)
899 					return ERROR;
900 
901 				result = grab_standard_service_macro_r(mac, macro_type, temp_service, output, free_macro);
902 				}
903 
904 			/* else and ondemand macro... */
905 			else {
906 
907 				/* if first arg is blank, it means use the current host name */
908 				if(arg1 == NULL || arg1[0] == '\x0') {
909 
910 					if(mac->host_ptr == NULL)
911 						return ERROR;
912 
913 					if((temp_service = find_service(mac->host_ptr->name, arg2))) {
914 
915 						/* get the service macro value */
916 						result = grab_standard_service_macro_r(mac, macro_type, temp_service, output, free_macro);
917 						}
918 					}
919 
920 				/* on-demand macro with both host and service name */
921 				else if((temp_service = find_service(arg1, arg2))) {
922 
923 					/* get the service macro value */
924 					result = grab_standard_service_macro_r(mac, macro_type, temp_service, output, free_macro);
925 					}
926 
927 				/* else we have a service macro with a servicegroup name and a delimiter... */
928 				else if(arg1 && arg2) {
929 
930 					if((temp_servicegroup = find_servicegroup(arg1)) == NULL) {
931 						if (macro_type == MACRO_SERVICESTATEID) {
932 							*output = strdup("3");
933 							return OK;
934 							}
935 						return ERROR;
936 						}
937 
938 					delimiter_len = strlen(arg2);
939 					*free_macro = TRUE;
940 
941 					/* concatenate macro values for all servicegroup members */
942 					for(temp_servicesmember = temp_servicegroup->members; temp_servicesmember != NULL; temp_servicesmember = temp_servicesmember->next) {
943 
944 						if((temp_service = temp_servicesmember->service_ptr) == NULL)
945 							continue;
946 
947 						/* get the macro value for this service */
948 						grab_standard_service_macro_r(mac, macro_type, temp_service, &temp_buffer, &free_sub_macro);
949 
950 						if(temp_buffer == NULL)
951 							continue;
952 
953 						/* add macro value to already running macro */
954 						if(*output == NULL)
955 							*output = (char *)strdup(temp_buffer);
956 						else {
957 							if((*output = (char *)realloc(*output, strlen(*output) + strlen(temp_buffer) + delimiter_len + 1)) == NULL)
958 								continue;
959 							strcat(*output, arg2);
960 							strcat(*output, temp_buffer);
961 							}
962 						if(free_sub_macro == TRUE)
963 							my_free(temp_buffer);
964 						}
965 					}
966 				else
967 					return ERROR;
968 				}
969 			break;
970 
971 			/***********************/
972 			/* SERVICEGROUP MACROS */
973 			/***********************/
974 		case MACRO_SERVICEGROUPMEMBERS:
975 		case MACRO_SERVICEGROUPNOTES:
976 		case MACRO_SERVICEGROUPNOTESURL:
977 		case MACRO_SERVICEGROUPACTIONURL:
978 			*free_macro = TRUE;
979 		case MACRO_SERVICEGROUPNAME:
980 		case MACRO_SERVICEGROUPALIAS:
981 			/* a standard servicegroup macro */
982 			/* use the saved servicegroup pointer */
983 			if(arg1 == NULL) {
984 				if((temp_servicegroup = mac->servicegroup_ptr) == NULL)
985 					return ERROR;
986 				}
987 
988 			/* else find the servicegroup for on-demand macros */
989 			else {
990 				if((temp_servicegroup = find_servicegroup(arg1)) == NULL)
991 					return ERROR;
992 				}
993 
994 			/* get the servicegroup macro value */
995 			result = grab_standard_servicegroup_macro_r(mac, macro_type, temp_servicegroup, output);
996 			break;
997 
998 			/******************/
999 			/* CONTACT MACROS */
1000 			/******************/
1001 		case MACRO_CONTACTGROUPNAMES:
1002 			*free_macro = TRUE;
1003 		case MACRO_CONTACTNAME:
1004 		case MACRO_CONTACTALIAS:
1005 		case MACRO_CONTACTEMAIL:
1006 		case MACRO_CONTACTPAGER:
1007 			/* a standard contact macro */
1008 			if(arg2 == NULL) {
1009 
1010 				/* find the contact for on-demand macros */
1011 				if(arg1) {
1012 					if((temp_contact = find_contact(arg1)) == NULL)
1013 						return ERROR;
1014 					}
1015 
1016 				/* else use saved contact pointer */
1017 				else if((temp_contact = mac->contact_ptr) == NULL)
1018 					return ERROR;
1019 
1020 				/* get the contact macro value */
1021 				result = grab_standard_contact_macro_r(mac, macro_type, temp_contact, output);
1022 				}
1023 
1024 			/* a contact macro with a contactgroup name and delimiter */
1025 			else {
1026 
1027 				if((temp_contactgroup = find_contactgroup(arg1)) == NULL)
1028 					return ERROR;
1029 
1030 				delimiter_len = strlen(arg2);
1031 				*free_macro = TRUE;
1032 
1033 				/* concatenate macro values for all contactgroup members */
1034 				for(temp_contactsmember = temp_contactgroup->members; temp_contactsmember != NULL; temp_contactsmember = temp_contactsmember->next) {
1035 
1036 					if((temp_contact = temp_contactsmember->contact_ptr) == NULL)
1037 						continue;
1038 
1039 					/* get the macro value for this contact */
1040 					grab_standard_contact_macro_r(mac, macro_type, temp_contact, &temp_buffer);
1041 
1042 					if(temp_buffer == NULL)
1043 						continue;
1044 
1045 					/* add macro value to already running macro */
1046 					if(*output == NULL)
1047 						*output = (char *)strdup(temp_buffer);
1048 					else {
1049 						if((*output = (char *)realloc(*output, strlen(*output) + strlen(temp_buffer) + delimiter_len + 1)) == NULL)
1050 							continue;
1051 						strcat(*output, arg2);
1052 						strcat(*output, temp_buffer);
1053 						}
1054 					my_free(temp_buffer);
1055 					}
1056 				}
1057 			break;
1058 
1059 			/***********************/
1060 			/* CONTACTGROUP MACROS */
1061 			/***********************/
1062 		case MACRO_CONTACTGROUPMEMBERS:
1063 			*free_macro = TRUE;
1064 		case MACRO_CONTACTGROUPNAME:
1065 		case MACRO_CONTACTGROUPALIAS:
1066 			/* a standard contactgroup macro */
1067 			/* use the saved contactgroup pointer */
1068 			if(arg1 == NULL) {
1069 				if((temp_contactgroup = mac->contactgroup_ptr) == NULL)
1070 					return ERROR;
1071 				}
1072 
1073 			/* else find the contactgroup for on-demand macros */
1074 			else {
1075 				if((temp_contactgroup = find_contactgroup(arg1)) == NULL)
1076 					return ERROR;
1077 				}
1078 
1079 			/* get the contactgroup macro value */
1080 			result = grab_standard_contactgroup_macro(macro_type, temp_contactgroup, output);
1081 			break;
1082 
1083 			/***********************/
1084 			/* NOTIFICATION MACROS */
1085 			/***********************/
1086 		case MACRO_NOTIFICATIONTYPE:
1087 		case MACRO_NOTIFICATIONNUMBER:
1088 		case MACRO_NOTIFICATIONRECIPIENTS:
1089 		case MACRO_NOTIFICATIONISESCALATED:
1090 		case MACRO_NOTIFICATIONAUTHOR:
1091 		case MACRO_NOTIFICATIONAUTHORNAME:
1092 		case MACRO_NOTIFICATIONAUTHORALIAS:
1093 		case MACRO_NOTIFICATIONCOMMENT:
1094 
1095 			/* notification macros have already been pre-computed */
1096 			*output = mac->x[macro_type];
1097 			*free_macro = FALSE;
1098 			break;
1099 
1100 			/********************/
1101 			/* DATE/TIME MACROS */
1102 			/********************/
1103 		case MACRO_LONGDATETIME:
1104 		case MACRO_SHORTDATETIME:
1105 		case MACRO_DATE:
1106 		case MACRO_TIME:
1107 			*free_macro = TRUE;
1108 		case MACRO_TIMET:
1109 		case MACRO_ISVALIDTIME:
1110 		case MACRO_NEXTVALIDTIME:
1111 
1112 			/* calculate macros */
1113 			result = grab_datetime_macro_r(mac, macro_type, arg1, arg2, output);
1114 			break;
1115 
1116 			/*****************/
1117 			/* STATIC MACROS */
1118 			/*****************/
1119 		case MACRO_ADMINEMAIL:
1120 		case MACRO_ADMINPAGER:
1121 		case MACRO_MAINCONFIGFILE:
1122 		case MACRO_STATUSDATAFILE:
1123 		case MACRO_RETENTIONDATAFILE:
1124 		case MACRO_OBJECTCACHEFILE:
1125 		case MACRO_TEMPFILE:
1126 		case MACRO_LOGFILE:
1127 		case MACRO_RESOURCEFILE:
1128 		case MACRO_COMMANDFILE:
1129 		case MACRO_HOSTPERFDATAFILE:
1130 		case MACRO_SERVICEPERFDATAFILE:
1131 		case MACRO_PROCESSSTARTTIME:
1132 		case MACRO_TEMPPATH:
1133 		case MACRO_EVENTSTARTTIME:
1134 
1135 			/* no need to do any more work - these are already precomputed for us */
1136 			*output = global_macros.x[macro_type];
1137 			*free_macro = FALSE;
1138 			break;
1139 
1140 			/******************/
1141 			/* SUMMARY MACROS */
1142 			/******************/
1143 		case MACRO_TOTALHOSTSUP:
1144 		case MACRO_TOTALHOSTSDOWN:
1145 		case MACRO_TOTALHOSTSUNREACHABLE:
1146 		case MACRO_TOTALHOSTSDOWNUNHANDLED:
1147 		case MACRO_TOTALHOSTSUNREACHABLEUNHANDLED:
1148 		case MACRO_TOTALHOSTPROBLEMS:
1149 		case MACRO_TOTALHOSTPROBLEMSUNHANDLED:
1150 		case MACRO_TOTALSERVICESOK:
1151 		case MACRO_TOTALSERVICESWARNING:
1152 		case MACRO_TOTALSERVICESCRITICAL:
1153 		case MACRO_TOTALSERVICESUNKNOWN:
1154 		case MACRO_TOTALSERVICESWARNINGUNHANDLED:
1155 		case MACRO_TOTALSERVICESCRITICALUNHANDLED:
1156 		case MACRO_TOTALSERVICESUNKNOWNUNHANDLED:
1157 		case MACRO_TOTALSERVICEPROBLEMS:
1158 		case MACRO_TOTALSERVICEPROBLEMSUNHANDLED:
1159 
1160 #ifdef NSCORE
1161 			/* generate summary macros if needed */
1162 			if(mac->x[MACRO_TOTALHOSTSUP] == NULL) {
1163 
1164 				/* get host totals */
1165 				for(temp_host = host_list; temp_host != NULL; temp_host = temp_host->next) {
1166 
1167 					/* filter totals based on contact if necessary */
1168 					if(mac->contact_ptr != NULL)
1169 						authorized = is_contact_for_host(temp_host, mac->contact_ptr);
1170 
1171 					if(authorized == TRUE) {
1172 						problem = TRUE;
1173 
1174 						if(temp_host->current_state == HOST_UP && temp_host->has_been_checked == TRUE)
1175 							hosts_up++;
1176 						else if(temp_host->current_state == HOST_DOWN) {
1177 							if(temp_host->scheduled_downtime_depth > 0)
1178 								problem = FALSE;
1179 							if(temp_host->problem_has_been_acknowledged == TRUE)
1180 								problem = FALSE;
1181 							if(temp_host->checks_enabled == FALSE)
1182 								problem = FALSE;
1183 							if(problem == TRUE)
1184 								hosts_down_unhandled++;
1185 							hosts_down++;
1186 							}
1187 						else if(temp_host->current_state == HOST_UNREACHABLE) {
1188 							if(temp_host->scheduled_downtime_depth > 0)
1189 								problem = FALSE;
1190 							if(temp_host->problem_has_been_acknowledged == TRUE)
1191 								problem = FALSE;
1192 							if(temp_host->checks_enabled == FALSE)
1193 								problem = FALSE;
1194 							if(problem == TRUE)
1195 								hosts_down_unhandled++;
1196 							hosts_unreachable++;
1197 							}
1198 						}
1199 					}
1200 
1201 				host_problems = hosts_down + hosts_unreachable;
1202 				host_problems_unhandled = hosts_down_unhandled + hosts_unreachable_unhandled;
1203 
1204 				/* get service totals */
1205 				for(temp_service = service_list; temp_service != NULL; temp_service = temp_service->next) {
1206 
1207 					/* filter totals based on contact if necessary */
1208 					if(mac->contact_ptr != NULL)
1209 						authorized = is_contact_for_service(temp_service, mac->contact_ptr);
1210 
1211 					if(authorized == TRUE) {
1212 						problem = TRUE;
1213 
1214 						if(temp_service->current_state == STATE_OK && temp_service->has_been_checked == TRUE)
1215 							services_ok++;
1216 						else if(temp_service->current_state == STATE_WARNING) {
1217 							temp_host = find_host(temp_service->host_name);
1218 							if(temp_host != NULL && (temp_host->current_state == HOST_DOWN || temp_host->current_state == HOST_UNREACHABLE))
1219 								problem = FALSE;
1220 							if(temp_service->scheduled_downtime_depth > 0)
1221 								problem = FALSE;
1222 							if(temp_service->problem_has_been_acknowledged == TRUE)
1223 								problem = FALSE;
1224 							if(temp_service->checks_enabled == FALSE)
1225 								problem = FALSE;
1226 							if(problem == TRUE)
1227 								services_warning_unhandled++;
1228 							services_warning++;
1229 							}
1230 						else if(temp_service->current_state == STATE_UNKNOWN) {
1231 							temp_host = find_host(temp_service->host_name);
1232 							if(temp_host != NULL && (temp_host->current_state == HOST_DOWN || temp_host->current_state == HOST_UNREACHABLE))
1233 								problem = FALSE;
1234 							if(temp_service->scheduled_downtime_depth > 0)
1235 								problem = FALSE;
1236 							if(temp_service->problem_has_been_acknowledged == TRUE)
1237 								problem = FALSE;
1238 							if(temp_service->checks_enabled == FALSE)
1239 								problem = FALSE;
1240 							if(problem == TRUE)
1241 								services_unknown_unhandled++;
1242 							services_unknown++;
1243 							}
1244 						else if(temp_service->current_state == STATE_CRITICAL) {
1245 							temp_host = find_host(temp_service->host_name);
1246 							if(temp_host != NULL && (temp_host->current_state == HOST_DOWN || temp_host->current_state == HOST_UNREACHABLE))
1247 								problem = FALSE;
1248 							if(temp_service->scheduled_downtime_depth > 0)
1249 								problem = FALSE;
1250 							if(temp_service->problem_has_been_acknowledged == TRUE)
1251 								problem = FALSE;
1252 							if(temp_service->checks_enabled == FALSE)
1253 								problem = FALSE;
1254 							if(problem == TRUE)
1255 								services_critical_unhandled++;
1256 							services_critical++;
1257 							}
1258 						}
1259 					}
1260 
1261 				service_problems = services_warning + services_critical + services_unknown;
1262 				service_problems_unhandled = services_warning_unhandled + services_critical_unhandled + services_unknown_unhandled;
1263 
1264 				/* these macros are time-intensive to compute, and will likely be used together, so save them all for future use */
1265 				for(x = MACRO_TOTALHOSTSUP; x <= MACRO_TOTALSERVICEPROBLEMSUNHANDLED; x++)
1266 					my_free(mac->x[x]);
1267 				asprintf(&mac->x[MACRO_TOTALHOSTSUP], "%d", hosts_up);
1268 				asprintf(&mac->x[MACRO_TOTALHOSTSDOWN], "%d", hosts_down);
1269 				asprintf(&mac->x[MACRO_TOTALHOSTSUNREACHABLE], "%d", hosts_unreachable);
1270 				asprintf(&mac->x[MACRO_TOTALHOSTSDOWNUNHANDLED], "%d", hosts_down_unhandled);
1271 				asprintf(&mac->x[MACRO_TOTALHOSTSUNREACHABLEUNHANDLED], "%d", hosts_unreachable_unhandled);
1272 				asprintf(&mac->x[MACRO_TOTALHOSTPROBLEMS], "%d", host_problems);
1273 				asprintf(&mac->x[MACRO_TOTALHOSTPROBLEMSUNHANDLED], "%d", host_problems_unhandled);
1274 				asprintf(&mac->x[MACRO_TOTALSERVICESOK], "%d", services_ok);
1275 				asprintf(&mac->x[MACRO_TOTALSERVICESWARNING], "%d", services_warning);
1276 				asprintf(&mac->x[MACRO_TOTALSERVICESCRITICAL], "%d", services_critical);
1277 				asprintf(&mac->x[MACRO_TOTALSERVICESUNKNOWN], "%d", services_unknown);
1278 				asprintf(&mac->x[MACRO_TOTALSERVICESWARNINGUNHANDLED], "%d", services_warning_unhandled);
1279 				asprintf(&mac->x[MACRO_TOTALSERVICESCRITICALUNHANDLED], "%d", services_critical_unhandled);
1280 				asprintf(&mac->x[MACRO_TOTALSERVICESUNKNOWNUNHANDLED], "%d", services_unknown_unhandled);
1281 				asprintf(&mac->x[MACRO_TOTALSERVICEPROBLEMS], "%d", service_problems);
1282 				asprintf(&mac->x[MACRO_TOTALSERVICEPROBLEMSUNHANDLED], "%d", service_problems_unhandled);
1283 				}
1284 
1285 			/* return only the macro the user requested */
1286 			*output = mac->x[macro_type];
1287 
1288 			/* tell caller to NOT free memory when done */
1289 			*free_macro = FALSE;
1290 #endif
1291 			break;
1292 
1293 		default:
1294 			log_debug_info(DEBUGL_MACROS, 0, "UNHANDLED MACRO #%d! THIS IS A BUG!\n", macro_type);
1295 			return ERROR;
1296 			break;
1297 		}
1298 
1299 	return result;
1300 	}
1301 
1302 int grab_macrox_value(int macro_type, char *arg1, char *arg2, char **output, int *free_macro) {
1303 	return grab_macrox_value_r(&global_macros, macro_type, arg1, arg2, output, free_macro);
1304 	}
1305 
1306 
1307 /* calculates the value of a custom macro */
1308 int grab_custom_macro_value_r(nagios_macros *mac, char *macro_name, char *arg1, char *arg2, char **output) {
1309 	host *temp_host = NULL;
1310 	hostgroup *temp_hostgroup = NULL;
1311 	hostsmember *temp_hostsmember = NULL;
1312 	service *temp_service = NULL;
1313 	servicegroup *temp_servicegroup = NULL;
1314 	servicesmember *temp_servicesmember = NULL;
1315 	contact *temp_contact = NULL;
1316 	contactgroup *temp_contactgroup = NULL;
1317 	contactsmember *temp_contactsmember = NULL;
1318 	int delimiter_len = 0;
1319 	char *temp_buffer = NULL;
1320 	int result = OK;
1321 
1322 	if(macro_name == NULL || output == NULL)
1323 		return ERROR;
1324 
1325 	/***** CUSTOM HOST MACRO *****/
1326 	if(strstr(macro_name, "_HOST") == macro_name) {
1327 
1328 		/* a standard host macro */
1329 		if(arg2 == NULL) {
1330 
1331 			/* find the host for on-demand macros */
1332 			if(arg1) {
1333 				if((temp_host = find_host(arg1)) == NULL)
1334 					return ERROR;
1335 				}
1336 
1337 			/* else use saved host pointer */
1338 			else if((temp_host = mac->host_ptr) == NULL)
1339 				return ERROR;
1340 
1341 			/* get the host macro value */
1342 			result = grab_custom_object_macro_r(mac, macro_name + 5, temp_host->custom_variables, output);
1343 			}
1344 
1345 		/* a host macro with a hostgroup name and delimiter */
1346 		else {
1347 			if((temp_hostgroup = find_hostgroup(arg1)) == NULL)
1348 				return ERROR;
1349 
1350 			delimiter_len = strlen(arg2);
1351 
1352 			/* concatenate macro values for all hostgroup members */
1353 			for(temp_hostsmember = temp_hostgroup->members; temp_hostsmember != NULL; temp_hostsmember = temp_hostsmember->next) {
1354 
1355 				if((temp_host = temp_hostsmember->host_ptr) == NULL)
1356 					continue;
1357 
1358 				/* get the macro value for this host */
1359 				grab_custom_macro_value_r(mac, macro_name, temp_host->name, NULL, &temp_buffer);
1360 
1361 				if(temp_buffer == NULL)
1362 					continue;
1363 
1364 				/* add macro value to already running macro */
1365 				if(*output == NULL)
1366 					*output = (char *)strdup(temp_buffer);
1367 				else {
1368 					if((*output = (char *)realloc(*output, strlen(*output) + strlen(temp_buffer) + delimiter_len + 1)) == NULL)
1369 						continue;
1370 					strcat(*output, arg2);
1371 					strcat(*output, temp_buffer);
1372 					}
1373 				my_free(temp_buffer);
1374 				}
1375 			}
1376 		}
1377 
1378 	/***** CUSTOM SERVICE MACRO *****/
1379 	else if(strstr(macro_name, "_SERVICE") == macro_name) {
1380 
1381 		/* use saved service pointer */
1382 		if(arg1 == NULL && arg2 == NULL) {
1383 
1384 			if((temp_service = mac->service_ptr) == NULL)
1385 				return ERROR;
1386 
1387 			/* get the service macro value */
1388 			result = grab_custom_object_macro_r(mac, macro_name + 8, temp_service->custom_variables, output);
1389 			}
1390 
1391 		/* else and ondemand macro... */
1392 		else {
1393 
1394 			/* if first arg is blank, it means use the current host name */
1395 			if(mac->host_ptr == NULL)
1396 				return ERROR;
1397 			if((temp_service = find_service((mac->host_ptr) ? mac->host_ptr->name : NULL, arg2))) {
1398 
1399 				/* get the service macro value */
1400 				result = grab_custom_object_macro_r(mac, macro_name + 8, temp_service->custom_variables, output);
1401 				}
1402 
1403 			/* else we have a service macro with a servicegroup name and a delimiter... */
1404 			else {
1405 
1406 				if((temp_servicegroup = find_servicegroup(arg1)) == NULL)
1407 					return ERROR;
1408 
1409 				delimiter_len = strlen(arg2);
1410 
1411 				/* concatenate macro values for all servicegroup members */
1412 				for(temp_servicesmember = temp_servicegroup->members; temp_servicesmember != NULL; temp_servicesmember = temp_servicesmember->next) {
1413 
1414 					if((temp_service = temp_servicesmember->service_ptr) == NULL)
1415 						continue;
1416 
1417 					/* get the macro value for this service */
1418 					grab_custom_macro_value_r(mac, macro_name, temp_service->host_name, temp_service->description, &temp_buffer);
1419 
1420 					if(temp_buffer == NULL)
1421 						continue;
1422 
1423 					/* add macro value to already running macro */
1424 					if(*output == NULL)
1425 						*output = (char *)strdup(temp_buffer);
1426 					else {
1427 						if((*output = (char *)realloc(*output, strlen(*output) + strlen(temp_buffer) + delimiter_len + 1)) == NULL)
1428 							continue;
1429 						strcat(*output, arg2);
1430 						strcat(*output, temp_buffer);
1431 						}
1432 					my_free(temp_buffer);
1433 					}
1434 				}
1435 			}
1436 		}
1437 
1438 	/***** CUSTOM CONTACT VARIABLE *****/
1439 	else if(strstr(macro_name, "_CONTACT") == macro_name) {
1440 
1441 		/* a standard contact macro */
1442 		if(arg2 == NULL) {
1443 
1444 			/* find the contact for on-demand macros */
1445 			if(arg1) {
1446 				if((temp_contact = find_contact(arg1)) == NULL)
1447 					return ERROR;
1448 				}
1449 
1450 			/* else use saved contact pointer */
1451 			else if((temp_contact = mac->contact_ptr) == NULL)
1452 				return ERROR;
1453 
1454 			/* get the contact macro value */
1455 			result = grab_custom_object_macro_r(mac, macro_name + 8, temp_contact->custom_variables, output);
1456 			}
1457 
1458 		/* a contact macro with a contactgroup name and delimiter */
1459 		else {
1460 
1461 			if((temp_contactgroup = find_contactgroup(arg1)) == NULL)
1462 				return ERROR;
1463 
1464 			delimiter_len = strlen(arg2);
1465 
1466 			/* concatenate macro values for all contactgroup members */
1467 			for(temp_contactsmember = temp_contactgroup->members; temp_contactsmember != NULL; temp_contactsmember = temp_contactsmember->next) {
1468 
1469 				if((temp_contact = temp_contactsmember->contact_ptr) == NULL)
1470 					continue;
1471 
1472 				/* get the macro value for this contact */
1473 				grab_custom_macro_value_r(mac, macro_name, temp_contact->name, NULL, &temp_buffer);
1474 
1475 				if(temp_buffer == NULL)
1476 					continue;
1477 
1478 				/* add macro value to already running macro */
1479 				if(*output == NULL)
1480 					*output = (char *)strdup(temp_buffer);
1481 				else {
1482 					if((*output = (char *)realloc(*output, strlen(*output) + strlen(temp_buffer) + delimiter_len + 1)) == NULL)
1483 						continue;
1484 					strcat(*output, arg2);
1485 					strcat(*output, temp_buffer);
1486 					}
1487 				my_free(temp_buffer);
1488 				}
1489 			}
1490 		}
1491 
1492 	else
1493 		return ERROR;
1494 
1495 	return result;
1496 	}
1497 
1498 int grab_custom_macro_value(char *macro_name, char *arg1, char *arg2, char **output) {
1499 	return grab_custom_macro_value_r(&global_macros, macro_name, arg1, arg2, output);
1500 	}
1501 
1502 
1503 /* calculates a date/time macro */
1504 int grab_datetime_macro_r(nagios_macros *mac, int macro_type, char *arg1, char *arg2, char **output) {
1505 	time_t current_time = 0L;
1506 	timeperiod *temp_timeperiod = NULL;
1507 #ifdef NSCORE
1508 	time_t test_time = 0L;
1509 	time_t next_valid_time = 0L;
1510 #endif
1511 
1512 	if(output == NULL)
1513 		return ERROR;
1514 
1515 	/* get the current time */
1516 	time(&current_time);
1517 
1518 	/* parse args, do prep work */
1519 	switch(macro_type) {
1520 
1521 		case MACRO_ISVALIDTIME:
1522 		case MACRO_NEXTVALIDTIME:
1523 
1524 			/* find the timeperiod */
1525 			if((temp_timeperiod = find_timeperiod(arg1)) == NULL)
1526 				return ERROR;
1527 
1528 			/* what timestamp should we use? */
1529 #ifdef NSCORE
1530 			if(arg2)
1531 				test_time = (time_t)strtoul(arg2, NULL, 0);
1532 			else
1533 				test_time = current_time;
1534 #endif
1535 			break;
1536 
1537 		default:
1538 			break;
1539 		}
1540 
1541 	/* calculate the value */
1542 	switch(macro_type) {
1543 
1544 		case MACRO_LONGDATETIME:
1545 			if(*output == NULL)
1546 				*output = (char *)malloc(MAX_DATETIME_LENGTH);
1547 			if(*output)
1548 				get_datetime_string(&current_time, *output, MAX_DATETIME_LENGTH, LONG_DATE_TIME);
1549 			break;
1550 
1551 		case MACRO_SHORTDATETIME:
1552 			if(*output == NULL)
1553 				*output = (char *)malloc(MAX_DATETIME_LENGTH);
1554 			if(*output)
1555 				get_datetime_string(&current_time, *output, MAX_DATETIME_LENGTH, SHORT_DATE_TIME);
1556 			break;
1557 
1558 		case MACRO_DATE:
1559 			if(*output == NULL)
1560 				*output = (char *)malloc(MAX_DATETIME_LENGTH);
1561 			if(*output)
1562 				get_datetime_string(&current_time, *output, MAX_DATETIME_LENGTH, SHORT_DATE);
1563 			break;
1564 
1565 		case MACRO_TIME:
1566 			if(*output == NULL)
1567 				*output = (char *)malloc(MAX_DATETIME_LENGTH);
1568 			if(*output)
1569 				get_datetime_string(&current_time, *output, MAX_DATETIME_LENGTH, SHORT_TIME);
1570 			break;
1571 
1572 		case MACRO_TIMET:
1573 			*output = (char *)mkstr("%lu", (unsigned long)current_time);
1574 			break;
1575 
1576 #ifdef NSCORE
1577 		case MACRO_ISVALIDTIME:
1578 			*output = (char *)mkstr("%d", (check_time_against_period(test_time, temp_timeperiod) == OK) ? 1 : 0);
1579 			break;
1580 
1581 		case MACRO_NEXTVALIDTIME:
1582 			get_next_valid_time(test_time, &next_valid_time, temp_timeperiod);
1583 			if(next_valid_time == test_time && check_time_against_period(test_time, temp_timeperiod) == ERROR)
1584 				next_valid_time = (time_t)0L;
1585 			*output = (char *)mkstr("%lu", (unsigned long)next_valid_time);
1586 			break;
1587 #endif
1588 
1589 		default:
1590 			return ERROR;
1591 			break;
1592 		}
1593 
1594 	return OK;
1595 	}
1596 
1597 int grab_datetime_macro(int macro_type, char *arg1, char *arg2, char **output) {
1598 	return grab_datetime_macro_r(&global_macros, macro_type, arg1, arg2, output);
1599 	}
1600 
1601 
1602 /* calculates a host macro */
1603 int grab_standard_host_macro_r(nagios_macros *mac, int macro_type, host *temp_host, char **output, int *free_macro) {
1604 	char *temp_buffer = NULL;
1605 #ifdef NSCORE
1606 	hostgroup *temp_hostgroup = NULL;
1607 	servicesmember *temp_servicesmember = NULL;
1608 	service *temp_service = NULL;
1609 	objectlist *temp_objectlist = NULL;
1610 	time_t current_time = 0L;
1611 	unsigned long duration = 0L;
1612 	int days = 0;
1613 	int hours = 0;
1614 	int minutes = 0;
1615 	int seconds = 0;
1616 	char *buf1 = NULL;
1617 	char *buf2 = NULL;
1618 	int total_host_services = 0;
1619 	int total_host_services_ok = 0;
1620 	int total_host_services_warning = 0;
1621 	int total_host_services_unknown = 0;
1622 	int total_host_services_critical = 0;
1623 #endif
1624 
1625 	if(temp_host == NULL || output == NULL || free_macro == NULL)
1626 		return ERROR;
1627 
1628 	/* get the macro */
1629 	switch(macro_type) {
1630 
1631 		case MACRO_HOSTNAME:
1632 			*output = temp_host->name;
1633 			break;
1634 		case MACRO_HOSTDISPLAYNAME:
1635 			if(temp_host->display_name)
1636 				*output = temp_host->display_name;
1637 			break;
1638 		case MACRO_HOSTALIAS:
1639 			*output = temp_host->alias;
1640 			break;
1641 		case MACRO_HOSTADDRESS:
1642 			*output = temp_host->address;
1643 			break;
1644 #ifdef NSCORE
1645 		case MACRO_HOSTSTATE:
1646 			*output = (char *)host_state_name(temp_host->current_state);
1647 			break;
1648 		case MACRO_HOSTSTATEID:
1649 			*output = (char *)mkstr("%d", temp_host->current_state);
1650 			break;
1651 		case MACRO_LASTHOSTSTATE:
1652 			*output = (char *)host_state_name(temp_host->last_state);
1653 			break;
1654 		case MACRO_LASTHOSTSTATEID:
1655 			*output = (char *)mkstr("%d", temp_host->last_state);
1656 			break;
1657 		case MACRO_HOSTCHECKTYPE:
1658 			*output = (char *)check_type_name(temp_host->check_type);
1659 			break;
1660 		case MACRO_HOSTSTATETYPE:
1661 			*output = (char *)state_type_name(temp_host->state_type);
1662 			break;
1663 		case MACRO_HOSTOUTPUT:
1664 			if(temp_host->plugin_output)
1665 				*output = temp_host->plugin_output;
1666 			break;
1667 		case MACRO_LONGHOSTOUTPUT:
1668 			if(temp_host->long_plugin_output)
1669 				*output = temp_host->long_plugin_output;
1670 			break;
1671 		case MACRO_HOSTPERFDATA:
1672 			if(temp_host->perf_data)
1673 				*output = temp_host->perf_data;
1674 			break;
1675 #endif
1676 		case MACRO_HOSTCHECKCOMMAND:
1677 			if(temp_host->check_command)
1678 				*output = temp_host->check_command;
1679 			break;
1680 #ifdef NSCORE
1681 		case MACRO_HOSTATTEMPT:
1682 			*output = (char *)mkstr("%d", temp_host->current_attempt);
1683 			break;
1684 		case MACRO_MAXHOSTATTEMPTS:
1685 			*output = (char *)mkstr("%d", temp_host->max_attempts);
1686 			break;
1687 		case MACRO_HOSTDOWNTIME:
1688 			*output = (char *)mkstr("%d", temp_host->scheduled_downtime_depth);
1689 			break;
1690 		case MACRO_HOSTPERCENTCHANGE:
1691 			*output = (char *)mkstr("%.2f", temp_host->percent_state_change);
1692 			break;
1693 		case MACRO_HOSTDURATIONSEC:
1694 		case MACRO_HOSTDURATION:
1695 			time(&current_time);
1696 			duration = (unsigned long)(current_time - temp_host->last_state_change);
1697 
1698 			if(macro_type == MACRO_HOSTDURATIONSEC)
1699 				*output = (char *)mkstr("%lu", duration);
1700 			else {
1701 
1702 				days = duration / 86400;
1703 				duration -= (days * 86400);
1704 				hours = duration / 3600;
1705 				duration -= (hours * 3600);
1706 				minutes = duration / 60;
1707 				duration -= (minutes * 60);
1708 				seconds = duration;
1709 				*output = (char *)mkstr("%dd %dh %dm %ds", days, hours, minutes, seconds);
1710 				}
1711 			break;
1712 		case MACRO_HOSTEXECUTIONTIME:
1713 			*output = (char *)mkstr("%.3f", temp_host->execution_time);
1714 			break;
1715 		case MACRO_HOSTLATENCY:
1716 			*output = (char *)mkstr("%.3f", temp_host->latency);
1717 			break;
1718 		case MACRO_LASTHOSTCHECK:
1719 			*output = (char *)mkstr("%lu", (unsigned long)temp_host->last_check);
1720 			break;
1721 		case MACRO_LASTHOSTSTATECHANGE:
1722 			*output = (char *)mkstr("%lu", (unsigned long)temp_host->last_state_change);
1723 			break;
1724 		case MACRO_LASTHOSTUP:
1725 			*output = (char *)mkstr("%lu", (unsigned long)temp_host->last_time_up);
1726 			break;
1727 		case MACRO_LASTHOSTDOWN:
1728 			*output = (char *)mkstr("%lu", (unsigned long)temp_host->last_time_down);
1729 			break;
1730 		case MACRO_LASTHOSTUNREACHABLE:
1731 			*output = (char *)mkstr("%lu", (unsigned long)temp_host->last_time_unreachable);
1732 			break;
1733 		case MACRO_HOSTNOTIFICATIONNUMBER:
1734 			*output = (char *)mkstr("%d", temp_host->current_notification_number);
1735 			break;
1736 		case MACRO_HOSTNOTIFICATIONID:
1737 			*output = (char *)mkstr("%lu", temp_host->current_notification_id);
1738 			break;
1739 		case MACRO_HOSTNOTIFICATIONENABLED:
1740 			*output = (char *)mkstr("%s", temp_host->notifications_enabled ? "YES" : "NO");
1741 			break;
1742 		case MACRO_HOSTNOTIFICATIONPERIOD:
1743 			*output = (char *)mkstr("%s", temp_host->notification_period);
1744 			break;
1745 		case MACRO_HOSTEVENTID:
1746 			*output = (char *)mkstr("%lu", temp_host->current_event_id);
1747 			break;
1748 		case MACRO_LASTHOSTEVENTID:
1749 			*output = (char *)mkstr("%lu", temp_host->last_event_id);
1750 			break;
1751 		case MACRO_HOSTPROBLEMID:
1752 			*output = (char *)mkstr("%lu", temp_host->current_problem_id);
1753 			break;
1754 		case MACRO_LASTHOSTPROBLEMID:
1755 			*output = (char *)mkstr("%lu", temp_host->last_problem_id);
1756 			break;
1757 #endif
1758 		case MACRO_HOSTACTIONURL:
1759 			if(temp_host->action_url)
1760 				*output = temp_host->action_url;
1761 			break;
1762 		case MACRO_HOSTNOTESURL:
1763 			if(temp_host->notes_url)
1764 				*output = temp_host->notes_url;
1765 			break;
1766 		case MACRO_HOSTNOTES:
1767 			if(temp_host->notes)
1768 				*output = temp_host->notes;
1769 			break;
1770 #ifdef NSCORE
1771 		case MACRO_HOSTGROUPNAMES:
1772 			/* find all hostgroups this host is associated with */
1773 			for(temp_objectlist = temp_host->hostgroups_ptr; temp_objectlist != NULL; temp_objectlist = temp_objectlist->next) {
1774 
1775 				if((temp_hostgroup = (hostgroup *)temp_objectlist->object_ptr) == NULL)
1776 					continue;
1777 
1778 				asprintf(&buf1, "%s%s%s", (buf2) ? buf2 : "", (buf2) ? "," : "", temp_hostgroup->group_name);
1779 				my_free(buf2);
1780 				buf2 = buf1;
1781 				}
1782 			if(buf2) {
1783 				*output = (char *)strdup(buf2);
1784 				my_free(buf2);
1785 				}
1786 			break;
1787 		case MACRO_TOTALHOSTSERVICES:
1788 		case MACRO_TOTALHOSTSERVICESOK:
1789 		case MACRO_TOTALHOSTSERVICESWARNING:
1790 		case MACRO_TOTALHOSTSERVICESUNKNOWN:
1791 		case MACRO_TOTALHOSTSERVICESCRITICAL:
1792 
1793 			/* generate host service summary macros (if they haven't already been computed) */
1794 			if(mac->x[MACRO_TOTALHOSTSERVICES] == NULL) {
1795 
1796 				for(temp_servicesmember = temp_host->services; temp_servicesmember != NULL; temp_servicesmember = temp_servicesmember->next) {
1797 					if((temp_service = temp_servicesmember->service_ptr) == NULL)
1798 						continue;
1799 
1800 					total_host_services++;
1801 
1802 					switch(temp_service->current_state) {
1803 						case STATE_OK:
1804 							total_host_services_ok++;
1805 							break;
1806 						case STATE_WARNING:
1807 							total_host_services_warning++;
1808 							break;
1809 						case STATE_UNKNOWN:
1810 							total_host_services_unknown++;
1811 							break;
1812 						case STATE_CRITICAL:
1813 							total_host_services_critical++;
1814 							break;
1815 						default:
1816 							break;
1817 						}
1818 					}
1819 
1820 				/* these macros are time-intensive to compute, and will likely be used together, so save them all for future use */
1821 				mac->x[MACRO_TOTALHOSTSERVICES] =
1822 						(char *)mkstr("%d", total_host_services);
1823 				mac->x[MACRO_TOTALHOSTSERVICESOK] =
1824 						(char *)mkstr("%d", total_host_services_ok);
1825 				mac->x[MACRO_TOTALHOSTSERVICESWARNING] =
1826 						(char *)mkstr("%d", total_host_services_warning);
1827 				mac->x[MACRO_TOTALHOSTSERVICESUNKNOWN] =
1828 						(char *)mkstr("%d", total_host_services_unknown);
1829 				mac->x[MACRO_TOTALHOSTSERVICESCRITICAL] =
1830 						(char *)mkstr("%d", total_host_services_critical);
1831 				}
1832 
1833 			/* return only the macro the user requested */
1834 			*output = mac->x[macro_type];
1835 			break;
1836 		case MACRO_HOSTIMPORTANCE:
1837 			*output = (char *)mkstr("%u", temp_host->hourly_value);
1838 			break;
1839 		case MACRO_HOSTANDSERVICESIMPORTANCE:
1840 			*output = (char *)mkstr("%u", temp_host->hourly_value +
1841 					host_services_value(temp_host));
1842 			break;
1843 		case MACRO_HOSTINFOURL:
1844 			*free_macro = TRUE;
1845 			buf1 = get_url_encoded_string(temp_host->name);
1846 			if (buf1 != NULL) {
1847 				asprintf(output, "%s/cgi-bin/extinfo.cgi?type=1&host=%s",
1848 					website_url ? website_url : "website_url not set",
1849 					buf1);
1850 				free(buf1);
1851 			}
1852 			break;
1853 #endif
1854 
1855 			/***************/
1856 			/* MISC MACROS */
1857 			/***************/
1858 		case MACRO_HOSTACKAUTHOR:
1859 		case MACRO_HOSTACKAUTHORNAME:
1860 		case MACRO_HOSTACKAUTHORALIAS:
1861 		case MACRO_HOSTACKCOMMENT:
1862 			/* no need to do any more work - these are already precomputed elsewhere */
1863 			/* NOTE: these macros won't work as on-demand macros */
1864 			*output = mac->x[macro_type];
1865 			break;
1866 
1867 		default:
1868 			log_debug_info(DEBUGL_MACROS, 0, "UNHANDLED HOST MACRO #%d! THIS IS A BUG!\n", macro_type);
1869 			return ERROR;
1870 			break;
1871 		}
1872 
1873 	/* post-processing */
1874 	/* notes, notes URL and action URL macros may themselves contain macros, so process them... */
1875 	switch(macro_type) {
1876 		case MACRO_HOSTACTIONURL:
1877 		case MACRO_HOSTNOTESURL:
1878 			*free_macro = TRUE;
1879 			process_macros_r(mac, *output, &temp_buffer, URL_ENCODE_MACRO_CHARS);
1880 			*output = temp_buffer;
1881 			break;
1882 		case MACRO_HOSTNOTES:
1883 			*free_macro = TRUE;
1884 			process_macros_r(mac, *output, &temp_buffer, 0);
1885 			*output = temp_buffer;
1886 			break;
1887 		default:
1888 			break;
1889 		}
1890 
1891 	return OK;
1892 	}
1893 
1894 int grab_standard_host_macro(int macro_type, host *temp_host, char **output, int *free_macro) {
1895 	return grab_standard_host_macro_r(&global_macros, macro_type, temp_host, output, free_macro);
1896 	}
1897 
1898 
1899 /* computes a hostgroup macro */
1900 int grab_standard_hostgroup_macro_r(nagios_macros *mac, int macro_type, hostgroup *temp_hostgroup, char **output) {
1901 	hostsmember *temp_hostsmember = NULL;
1902 	char *temp_buffer = NULL;
1903 	unsigned int	temp_len = 0;
1904 
1905 	if(temp_hostgroup == NULL || output == NULL)
1906 		return ERROR;
1907 
1908 	/* get the macro value */
1909 	switch(macro_type) {
1910 		case MACRO_HOSTGROUPNAME:
1911 			*output = temp_hostgroup->group_name;
1912 			break;
1913 		case MACRO_HOSTGROUPALIAS:
1914 			if(temp_hostgroup->alias)
1915 				*output = temp_hostgroup->alias;
1916 			break;
1917 		case MACRO_HOSTGROUPMEMBERS:
1918 		case MACRO_HOSTGROUPMEMBERADDRESSES:
1919 			/* make the calculations for total string length */
1920 			for(temp_hostsmember = temp_hostgroup->members; temp_hostsmember != NULL; temp_hostsmember = temp_hostsmember->next) {
1921 				if(macro_type == MACRO_HOSTGROUPMEMBERS) {
1922 					if(temp_hostsmember->host_name != NULL)
1923 						temp_len += strlen(temp_hostsmember->host_name) + 2;
1924 					}
1925 				else {
1926 					if(temp_hostsmember->host_ptr->address != NULL)
1927 						temp_len += strlen(temp_hostsmember->host_ptr->address) + 2;
1928 					}
1929 				}
1930 			if(!temp_len) {
1931 				/* empty group, so return the nul string */
1932 				*output = calloc(1, 1);
1933 				return OK;
1934 				}
1935 
1936 			/* allocate or reallocate the memory buffer */
1937 			if(*output == NULL) {
1938 				*output = (char *)malloc(temp_len);
1939 				**output = '\0';
1940 				}
1941 			else {
1942 				temp_len += strlen(*output);
1943 				*output = (char *)realloc(*output, temp_len);
1944 				}
1945 			/* now fill in the string with the member names or addresses */
1946 			for(temp_hostsmember = temp_hostgroup->members; temp_hostsmember != NULL; temp_hostsmember = temp_hostsmember->next) {
1947 				if(macro_type == MACRO_HOSTGROUPMEMBERS) {
1948 					if(temp_hostsmember->host_name == NULL)
1949 						continue;
1950 					}
1951 				else {
1952 					if(temp_hostsmember->host_ptr->address == NULL)
1953 						continue;
1954 					}
1955 				if(**output != '\0')
1956 					strcat(*output, ",");
1957 				if(macro_type == MACRO_HOSTGROUPMEMBERS)
1958 					strcat(*output, temp_hostsmember->host_name);
1959 				else
1960 					strcat(*output, temp_hostsmember->host_ptr->address);
1961 				}
1962 			break;
1963 		case MACRO_HOSTGROUPACTIONURL:
1964 			if(temp_hostgroup->action_url)
1965 				*output = temp_hostgroup->action_url;
1966 			break;
1967 		case MACRO_HOSTGROUPNOTESURL:
1968 			if(temp_hostgroup->notes_url)
1969 				*output = temp_hostgroup->notes_url;
1970 			break;
1971 		case MACRO_HOSTGROUPNOTES:
1972 			if(temp_hostgroup->notes)
1973 				*output = temp_hostgroup->notes;
1974 			break;
1975 		default:
1976 			log_debug_info(DEBUGL_MACROS, 0, "UNHANDLED HOSTGROUP MACRO #%d! THIS IS A BUG!\n", macro_type);
1977 			return ERROR;
1978 			break;
1979 		}
1980 
1981 	/* post-processing */
1982 	/* notes, notes URL and action URL macros may themselves contain macros, so process them... */
1983 	switch(macro_type) {
1984 		case MACRO_HOSTGROUPACTIONURL:
1985 		case MACRO_HOSTGROUPNOTESURL:
1986 			process_macros_r(mac, *output, &temp_buffer, URL_ENCODE_MACRO_CHARS);
1987 			*output = temp_buffer;
1988 			break;
1989 		case MACRO_HOSTGROUPNOTES:
1990 			process_macros_r(mac, *output, &temp_buffer, 0);
1991 			*output = temp_buffer;
1992 			break;
1993 		default:
1994 			break;
1995 		}
1996 
1997 	return OK;
1998 	}
1999 
2000 int grab_standard_hostgroup_macro(int macro_type, hostgroup *temp_hostgroup, char **output) {
2001 	return grab_standard_hostgroup_macro_r(&global_macros, macro_type, temp_hostgroup, output);
2002 	}
2003 
2004 
2005 /* computes a service macro */
2006 int grab_standard_service_macro_r(nagios_macros *mac, int macro_type, service *temp_service, char **output, int *free_macro) {
2007 	char *temp_buffer = NULL;
2008 #ifdef NSCORE
2009 	servicegroup *temp_servicegroup = NULL;
2010 	objectlist *temp_objectlist = NULL;
2011 	time_t current_time = 0L;
2012 	unsigned long duration = 0L;
2013 	int days = 0;
2014 	int hours = 0;
2015 	int minutes = 0;
2016 	int seconds = 0;
2017 	char *buf1 = NULL;
2018 	char *buf2 = NULL;
2019 #endif
2020 
2021 	if(temp_service == NULL || output == NULL)
2022 		return ERROR;
2023 
2024 	/* get the macro value */
2025 	switch(macro_type) {
2026 		case MACRO_SERVICEDESC:
2027 			*output = temp_service->description;
2028 			break;
2029 		case MACRO_SERVICEDISPLAYNAME:
2030 			if(temp_service->display_name)
2031 				*output = temp_service->display_name;
2032 			break;
2033 #ifdef NSCORE
2034 		case MACRO_SERVICEOUTPUT:
2035 			if(temp_service->plugin_output)
2036 				*output = temp_service->plugin_output;
2037 			break;
2038 		case MACRO_LONGSERVICEOUTPUT:
2039 			if(temp_service->long_plugin_output)
2040 				*output = temp_service->long_plugin_output;
2041 			break;
2042 		case MACRO_SERVICEPERFDATA:
2043 			if(temp_service->perf_data)
2044 				*output = temp_service->perf_data;
2045 			break;
2046 #endif
2047 		case MACRO_SERVICECHECKCOMMAND:
2048 			if(temp_service->check_command)
2049 				*output = temp_service->check_command;
2050 			break;
2051 #ifdef NSCORE
2052 		case MACRO_SERVICECHECKTYPE:
2053 			*output = (char *)check_type_name(temp_service->check_type);
2054 			break;
2055 		case MACRO_SERVICESTATETYPE:
2056 			*output = (char *)state_type_name(temp_service->state_type);
2057 			break;
2058 		case MACRO_SERVICESTATE:
2059 			*output = (char *)service_state_name(temp_service->current_state);
2060 			break;
2061 		case MACRO_SERVICESTATEID:
2062 			*output = (char *)mkstr("%d", temp_service->current_state);
2063 			break;
2064 		case MACRO_LASTSERVICESTATE:
2065 			*output = (char *)service_state_name(temp_service->last_state);
2066 			break;
2067 		case MACRO_LASTSERVICESTATEID:
2068 			*output = (char *)mkstr("%d", temp_service->last_state);
2069 			break;
2070 #endif
2071 		case MACRO_SERVICEISVOLATILE:
2072 			*output = (char *)mkstr("%d", temp_service->is_volatile);
2073 			break;
2074 #ifdef NSCORE
2075 		case MACRO_SERVICEATTEMPT:
2076 			*output = (char *)mkstr("%d", temp_service->current_attempt);
2077 			break;
2078 		case MACRO_MAXSERVICEATTEMPTS:
2079 			*output = (char *)mkstr("%d", temp_service->max_attempts);
2080 			break;
2081 		case MACRO_SERVICEEXECUTIONTIME:
2082 			*output = (char *)mkstr("%.3f", temp_service->execution_time);
2083 			break;
2084 		case MACRO_SERVICELATENCY:
2085 			*output = (char *)mkstr("%.3f", temp_service->latency);
2086 			break;
2087 		case MACRO_LASTSERVICECHECK:
2088 			*output = (char *)mkstr("%lu", (unsigned long)temp_service->last_check);
2089 			break;
2090 		case MACRO_LASTSERVICESTATECHANGE:
2091 			*output = (char *)mkstr("%lu", (unsigned long)temp_service->last_state_change);
2092 			break;
2093 		case MACRO_LASTSERVICEOK:
2094 			*output = (char *)mkstr("%lu", (unsigned long)temp_service->last_time_ok);
2095 			break;
2096 		case MACRO_LASTSERVICEWARNING:
2097 			*output = (char *)mkstr("%lu", (unsigned long)temp_service->last_time_warning);
2098 			break;
2099 		case MACRO_LASTSERVICEUNKNOWN:
2100 			*output = (char *)mkstr("%lu", (unsigned long)temp_service->last_time_unknown);
2101 			break;
2102 		case MACRO_LASTSERVICECRITICAL:
2103 			*output = (char *)mkstr("%lu", (unsigned long)temp_service->last_time_critical);
2104 			break;
2105 		case MACRO_SERVICEDOWNTIME:
2106 			*output = (char *)mkstr("%d", temp_service->scheduled_downtime_depth);
2107 			break;
2108 		case MACRO_SERVICEPERCENTCHANGE:
2109 			*output = (char *)mkstr("%.2f", temp_service->percent_state_change);
2110 			break;
2111 		case MACRO_SERVICEDURATIONSEC:
2112 		case MACRO_SERVICEDURATION:
2113 
2114 			time(&current_time);
2115 			duration = (unsigned long)(current_time - temp_service->last_state_change);
2116 
2117 			/* get the state duration in seconds */
2118 			if(macro_type == MACRO_SERVICEDURATIONSEC)
2119 				*output = (char *)mkstr("%lu", duration);
2120 
2121 			/* get the state duration */
2122 			else {
2123 				days = duration / 86400;
2124 				duration -= (days * 86400);
2125 				hours = duration / 3600;
2126 				duration -= (hours * 3600);
2127 				minutes = duration / 60;
2128 				duration -= (minutes * 60);
2129 				seconds = duration;
2130 				*output = (char *)mkstr("%dd %dh %dm %ds", days, hours, minutes, seconds);
2131 				}
2132 			break;
2133 		case MACRO_SERVICENOTIFICATIONNUMBER:
2134 			*output = (char *)mkstr("%d", temp_service->current_notification_number);
2135 			break;
2136 		case MACRO_SERVICENOTIFICATIONID:
2137 			*output = (char *)mkstr("%lu", temp_service->current_notification_id);
2138 			break;
2139 		case MACRO_SERVICENOTIFICATIONENABLED:
2140 			*output = (char *)mkstr("%s", temp_service->notifications_enabled ? "YES" : "NO");
2141 			break;
2142 		case MACRO_SERVICENOTIFICATIONPERIOD:
2143 			*output = (char *)mkstr("%s", temp_service->notification_period);
2144 			break;
2145 		case MACRO_SERVICEEVENTID:
2146 			*output = (char *)mkstr("%lu", temp_service->current_event_id);
2147 			break;
2148 		case MACRO_LASTSERVICEEVENTID:
2149 			*output = (char *)mkstr("%lu", temp_service->last_event_id);
2150 			break;
2151 		case MACRO_SERVICEPROBLEMID:
2152 			*output = (char *)mkstr("%lu", temp_service->current_problem_id);
2153 			break;
2154 		case MACRO_LASTSERVICEPROBLEMID:
2155 			*output = (char *)mkstr("%lu", temp_service->last_problem_id);
2156 			break;
2157 #endif
2158 		case MACRO_SERVICEACTIONURL:
2159 			if(temp_service->action_url)
2160 				*output = temp_service->action_url;
2161 			break;
2162 		case MACRO_SERVICENOTESURL:
2163 			if(temp_service->notes_url)
2164 				*output = temp_service->notes_url;
2165 			break;
2166 		case MACRO_SERVICENOTES:
2167 			if(temp_service->notes)
2168 				*output = temp_service->notes;
2169 			break;
2170 
2171 #ifdef NSCORE
2172 		case MACRO_SERVICEGROUPNAMES:
2173 			/* find all servicegroups this service is associated with */
2174 			for(temp_objectlist = temp_service->servicegroups_ptr; temp_objectlist != NULL; temp_objectlist = temp_objectlist->next) {
2175 
2176 				if((temp_servicegroup = (servicegroup *)temp_objectlist->object_ptr) == NULL)
2177 					continue;
2178 
2179 				asprintf(&buf1, "%s%s%s", (buf2) ? buf2 : "", (buf2) ? "," : "", temp_servicegroup->group_name);
2180 				my_free(buf2);
2181 				buf2 = buf1;
2182 				}
2183 			if(buf2) {
2184 				*output = (char *)strdup(buf2);
2185 				my_free(buf2);
2186 				}
2187 			break;
2188 		case MACRO_SERVICEIMPORTANCE:
2189 			*output = (char *)mkstr("%u", temp_service->hourly_value);
2190 			break;
2191 		case MACRO_SERVICEINFOURL:
2192 			*free_macro = TRUE;
2193 			buf1 = get_url_encoded_string(temp_service->host_name);
2194 			buf2 = get_url_encoded_string(temp_service->description);
2195 			if (buf1 != NULL) {
2196 				if (buf2 != NULL) {
2197 					asprintf(output, "%s/cgi-bin/extinfo.cgi?type=2&host=%s&service=%s",
2198 						website_url ? website_url : "website_url not set",
2199 						buf1, buf2);
2200 					free(buf2);
2201 				}
2202 				free(buf1);
2203 			}
2204 			break;
2205 #endif
2206 
2207 			/***************/
2208 			/* MISC MACROS */
2209 			/***************/
2210 		case MACRO_SERVICEACKAUTHOR:
2211 		case MACRO_SERVICEACKAUTHORNAME:
2212 		case MACRO_SERVICEACKAUTHORALIAS:
2213 		case MACRO_SERVICEACKCOMMENT:
2214 			/* no need to do any more work - these are already precomputed elsewhere */
2215 			/* NOTE: these macros won't work as on-demand macros */
2216 			*output = mac->x[macro_type];
2217 			*free_macro = FALSE;
2218 			break;
2219 
2220 		default:
2221 			log_debug_info(DEBUGL_MACROS, 0, "UNHANDLED SERVICE MACRO #%d! THIS IS A BUG!\n", macro_type);
2222 			return ERROR;
2223 			break;
2224 		}
2225 
2226 	/* post-processing */
2227 	/* notes, notes URL and action URL macros may themselves contain macros, so process them... */
2228 	switch(macro_type) {
2229 		case MACRO_SERVICEACTIONURL:
2230 		case MACRO_SERVICENOTESURL:
2231 			*free_macro = TRUE;
2232 			process_macros_r(mac, *output, &temp_buffer, URL_ENCODE_MACRO_CHARS);
2233 			*output = temp_buffer;
2234 			break;
2235 		case MACRO_SERVICENOTES:
2236 			process_macros_r(mac, *output, &temp_buffer, 0);
2237 			*output = temp_buffer;
2238 			break;
2239 		default:
2240 			break;
2241 		}
2242 
2243 	return OK;
2244 	}
2245 
2246 int grab_standard_service_macro(int macro_type, service *temp_service, char **output, int *free_macro) {
2247 	return grab_standard_service_macro_r(&global_macros, macro_type, temp_service, output, free_macro);
2248 	}
2249 
2250 
2251 /* computes a servicegroup macro */
2252 int grab_standard_servicegroup_macro_r(nagios_macros *mac, int macro_type, servicegroup *temp_servicegroup, char **output) {
2253 	servicesmember *temp_servicesmember = NULL;
2254 	char *temp_buffer = NULL;
2255 	unsigned int	temp_len = 0;
2256 	unsigned int	init_len = 0;
2257 
2258 	if(temp_servicegroup == NULL || output == NULL)
2259 		return ERROR;
2260 
2261 	/* get the macro value */
2262 	switch(macro_type) {
2263 		case MACRO_SERVICEGROUPNAME:
2264 			*output = temp_servicegroup->group_name;
2265 			break;
2266 		case MACRO_SERVICEGROUPALIAS:
2267 			if(temp_servicegroup->alias)
2268 				*output = temp_servicegroup->alias;
2269 			break;
2270 		case MACRO_SERVICEGROUPMEMBERS:
2271 			/* make the calculations for total string length */
2272 			for(temp_servicesmember = temp_servicegroup->members; temp_servicesmember != NULL; temp_servicesmember = temp_servicesmember->next) {
2273 				if(temp_servicesmember->host_name == NULL || temp_servicesmember->service_description == NULL)
2274 					continue;
2275 				if(temp_len == 0) {
2276 					temp_len += strlen(temp_servicesmember->host_name) + strlen(temp_servicesmember->service_description) + 2;
2277 					}
2278 				else {
2279 					temp_len += strlen(temp_servicesmember->host_name) + strlen(temp_servicesmember->service_description) + 3;
2280 					}
2281 				}
2282 			if(!temp_len) {
2283 				/* empty group, so return the nul string */
2284 				*output = calloc(1, 1);
2285 				return OK;
2286 				}
2287 			/* allocate or reallocate the memory buffer */
2288 			if(*output == NULL) {
2289 				*output = (char *)malloc(temp_len);
2290 				}
2291 			else {
2292 				init_len = strlen(*output);
2293 				temp_len += init_len;
2294 				*output = (char *)realloc(*output, temp_len);
2295 				}
2296 			/* now fill in the string with the group members */
2297 			for(temp_servicesmember = temp_servicegroup->members; temp_servicesmember != NULL; temp_servicesmember = temp_servicesmember->next) {
2298 				if(temp_servicesmember->host_name == NULL || temp_servicesmember->service_description == NULL)
2299 					continue;
2300 				temp_buffer = *output + init_len;
2301 				if(init_len == 0) {  /* If our buffer didn't contain anything, we just need to write "%s,%s" */
2302 					init_len += sprintf(temp_buffer, "%s,%s", temp_servicesmember->host_name, temp_servicesmember->service_description);
2303 					}
2304 				else {   /* Now we need to write ",%s,%s" */
2305 					init_len += sprintf(temp_buffer, ",%s,%s", temp_servicesmember->host_name, temp_servicesmember->service_description);
2306 					}
2307 				}
2308 			break;
2309 		case MACRO_SERVICEGROUPACTIONURL:
2310 			if(temp_servicegroup->action_url)
2311 				*output = temp_servicegroup->action_url;
2312 			break;
2313 		case MACRO_SERVICEGROUPNOTESURL:
2314 			if(temp_servicegroup->notes_url)
2315 				*output = temp_servicegroup->notes_url;
2316 			break;
2317 		case MACRO_SERVICEGROUPNOTES:
2318 			if(temp_servicegroup->notes)
2319 				*output = temp_servicegroup->notes;
2320 			break;
2321 		default:
2322 			log_debug_info(DEBUGL_MACROS, 0, "UNHANDLED SERVICEGROUP MACRO #%d! THIS IS A BUG!\n", macro_type);
2323 			return ERROR;
2324 		}
2325 
2326 	/* post-processing */
2327 	/* notes, notes URL and action URL macros may themselves contain macros, so process them... */
2328 	switch(macro_type) {
2329 		case MACRO_SERVICEGROUPACTIONURL:
2330 		case MACRO_SERVICEGROUPNOTESURL:
2331 			process_macros_r(mac, *output, &temp_buffer, URL_ENCODE_MACRO_CHARS);
2332 			*output = temp_buffer;
2333 			break;
2334 		case MACRO_SERVICEGROUPNOTES:
2335 			process_macros_r(mac, *output, &temp_buffer, 0);
2336 			*output = temp_buffer;
2337 			break;
2338 		default:
2339 			break;
2340 		}
2341 
2342 	return OK;
2343 	}
2344 
2345 int grab_standard_servicegroup_macro(int macro_type, servicegroup *temp_servicegroup, char **output) {
2346 	return grab_standard_servicegroup_macro_r(&global_macros, macro_type, temp_servicegroup, output);
2347 	}
2348 
2349 
2350 /* computes a contact macro */
2351 int grab_standard_contact_macro_r(nagios_macros *mac, int macro_type, contact *temp_contact, char **output) {
2352 #ifdef NSCORE
2353 	contactgroup *temp_contactgroup = NULL;
2354 	objectlist *temp_objectlist = NULL;
2355 	char *buf1 = NULL;
2356 	char *buf2 = NULL;
2357 #endif
2358 
2359 	if(temp_contact == NULL || output == NULL)
2360 		return ERROR;
2361 
2362 	/* get the macro value */
2363 	switch(macro_type) {
2364 		case MACRO_CONTACTNAME:
2365 			*output = temp_contact->name;
2366 			break;
2367 		case MACRO_CONTACTALIAS:
2368 			*output = temp_contact->alias;
2369 			break;
2370 		case MACRO_CONTACTEMAIL:
2371 			if(temp_contact->email)
2372 				*output = temp_contact->email;
2373 			break;
2374 		case MACRO_CONTACTPAGER:
2375 			if(temp_contact->pager)
2376 				*output = temp_contact->pager;
2377 			break;
2378 #ifdef NSCORE
2379 		case MACRO_CONTACTGROUPNAMES:
2380 			/* get the contactgroup names */
2381 			/* find all contactgroups this contact is a member of */
2382 			for(temp_objectlist = temp_contact->contactgroups_ptr; temp_objectlist != NULL; temp_objectlist = temp_objectlist->next) {
2383 
2384 				if((temp_contactgroup = (contactgroup *)temp_objectlist->object_ptr) == NULL)
2385 					continue;
2386 
2387 				asprintf(&buf1, "%s%s%s", (buf2) ? buf2 : "", (buf2) ? "," : "", temp_contactgroup->group_name);
2388 				my_free(buf2);
2389 				buf2 = buf1;
2390 				}
2391 			if(buf2) {
2392 				*output = (char *)strdup(buf2);
2393 				my_free(buf2);
2394 				}
2395 			break;
2396 #endif
2397 		default:
2398 			log_debug_info(DEBUGL_MACROS, 0, "UNHANDLED CONTACT MACRO #%d! THIS IS A BUG!\n", macro_type);
2399 			return ERROR;
2400 		}
2401 
2402 	return OK;
2403 	}
2404 
2405 int grab_standard_contact_macro(int macro_type, contact *temp_contact, char **output) {
2406 	return grab_standard_contact_macro_r(&global_macros, macro_type, temp_contact, output);
2407 	}
2408 
2409 
2410 /* computes a contact address macro */
2411 int grab_contact_address_macro(int macro_num, contact *temp_contact, char **output) {
2412 	if(macro_num < 0 || macro_num >= MAX_CONTACT_ADDRESSES)
2413 		return ERROR;
2414 
2415 	if(temp_contact == NULL || output == NULL)
2416 		return ERROR;
2417 
2418 	/* get the macro */
2419 	if(temp_contact->address[macro_num])
2420 		*output = temp_contact->address[macro_num];
2421 
2422 	return OK;
2423 	}
2424 
2425 
2426 
2427 /* computes a contactgroup macro */
2428 int grab_standard_contactgroup_macro(int macro_type, contactgroup *temp_contactgroup, char **output) {
2429 	contactsmember *temp_contactsmember = NULL;
2430 
2431 	if(temp_contactgroup == NULL || output == NULL)
2432 		return ERROR;
2433 
2434 	/* get the macro value */
2435 	switch(macro_type) {
2436 		case MACRO_CONTACTGROUPNAME:
2437 			*output = temp_contactgroup->group_name;
2438 			break;
2439 		case MACRO_CONTACTGROUPALIAS:
2440 			if(temp_contactgroup->alias)
2441 				*output = temp_contactgroup->alias;
2442 			break;
2443 		case MACRO_CONTACTGROUPMEMBERS:
2444 			/* get the member list */
2445 			for(temp_contactsmember = temp_contactgroup->members; temp_contactsmember != NULL; temp_contactsmember = temp_contactsmember->next) {
2446 				if(temp_contactsmember->contact_name == NULL)
2447 					continue;
2448 				if(*output == NULL)
2449 					*output = (char *)strdup(temp_contactsmember->contact_name);
2450 				else if((*output = (char *)realloc(*output, strlen(*output) + strlen(temp_contactsmember->contact_name) + 2))) {
2451 					strcat(*output, ",");
2452 					strcat(*output, temp_contactsmember->contact_name);
2453 					}
2454 				}
2455 			break;
2456 		default:
2457 			log_debug_info(DEBUGL_MACROS, 0, "UNHANDLED CONTACTGROUP MACRO #%d! THIS IS A BUG!\n", macro_type);
2458 			return ERROR;
2459 		}
2460 
2461 	return OK;
2462 	}
2463 
2464 
2465 /* computes a custom object macro */
2466 int grab_custom_object_macro_r(nagios_macros *mac, char *macro_name, customvariablesmember *vars, char **output) {
2467 	customvariablesmember *temp_customvariablesmember = NULL;
2468 	int result = ERROR;
2469 
2470 	if(macro_name == NULL || vars == NULL || output == NULL)
2471 		return ERROR;
2472 
2473 	/* get the custom variable */
2474 	for(temp_customvariablesmember = vars; temp_customvariablesmember != NULL; temp_customvariablesmember = temp_customvariablesmember->next) {
2475 
2476 		if(temp_customvariablesmember->variable_name == NULL)
2477 			continue;
2478 
2479 		if(!strcmp(macro_name, temp_customvariablesmember->variable_name)) {
2480 			if(temp_customvariablesmember->variable_value)
2481 				*output = temp_customvariablesmember->variable_value;
2482 			result = OK;
2483 			break;
2484 			}
2485 		}
2486 
2487 	return result;
2488 	}
2489 
2490 int grab_custom_object_macro(char *macro_name, customvariablesmember *vars, char **output) {
2491 	return grab_custom_object_macro_r(&global_macros, macro_name, vars, output);
2492 	}
2493 
2494 
2495 /******************************************************************/
2496 /********************* MACRO STRING FUNCTIONS *********************/
2497 /******************************************************************/
2498 
2499 /* cleans illegal characters in macros before output */
2500 char *clean_macro_chars(char *macro, int options) {
2501 	register int x = 0;
2502 	register int y = 0;
2503 	register int ch = 0;
2504 	register int len = 0;
2505 	char *ret = NULL;
2506 
2507 	if(macro == NULL || !*macro)
2508 		return "";
2509 
2510 	len = (int)strlen(macro);
2511 	ret = strdup(macro);
2512 
2513 	/* strip illegal characters out of macro */
2514 	if(options & STRIP_ILLEGAL_MACRO_CHARS) {
2515 		for(y = 0, x = 0; x < len; x++) {
2516 			ch = macro[x] & 0xff;
2517 
2518 			/* illegal chars are skipped */
2519 			if(!illegal_output_char_map[ch])
2520 				ret[y++] = ret[x];
2521 			}
2522 
2523 		ret[y++] = '\x0';
2524 		}
2525 
2526 	return ret;
2527 	}
2528 
2529 
2530 
2531 /* encodes a string in proper URL format */
2532 char *get_url_encoded_string(char *input) {
2533 	/* From RFC 3986:
2534 	segment       = *pchar
2535 
2536 	[...]
2537 
2538 	pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
2539 
2540 	query         = *( pchar / "/" / "?" )
2541 
2542 	fragment      = *( pchar / "/" / "?" )
2543 
2544 	pct-encoded   = "%" HEXDIG HEXDIG
2545 
2546 	unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
2547 	reserved      = gen-delims / sub-delims
2548 	gen-delims    = ":" / "/" / "?" / "#" / "[" / "]" / "@"
2549 	sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
2550 	                 / "*" / "+" / "," / ";" / "="
2551 
2552 	Encode everything but "unreserved", to be on safe side.
2553 
2554 	Another note:
2555 	nowhere in the RFC states that + is interpreted as space. Therefore, encode
2556 	space as %20 (as all other characters that should be escaped)
2557 	*/
2558 
2559 	register int x = 0;
2560 	register int y = 0;
2561 	char *encoded_url_string = NULL;
2562 
2563 
2564 	/* bail if no input */
2565 	if(input == NULL)
2566 		return NULL;
2567 
2568 	/* allocate enough memory to escape all characters if necessary */
2569 	if((encoded_url_string = (char *)malloc((strlen(input) * 3) + 1)) == NULL)
2570 		return NULL;
2571 
2572 	/* check/encode all characters */
2573 	for(x = 0, y = 0; input[x]; x++) {
2574 
2575 		/* alpha-numeric characters and a few other characters don't get encoded */
2576 		if(((char)input[x] >= '0' && (char)input[x] <= '9') ||
2577 		   ((char)input[x] >= 'A' && (char)input[x] <= 'Z') ||
2578 		   ((char)input[x] >= 'a' && (char)input[x] <= 'z') ||
2579 		   (char)input[x] == '.' ||
2580 		   (char)input[x] == '-' ||
2581 		   (char)input[x] == '_' ||
2582 		   (char)input[x] == '~') {
2583 
2584 			encoded_url_string[y++] = input[x];
2585 			}
2586 
2587 		/* anything else gets represented by its hex value */
2588 		else {
2589 			sprintf(&encoded_url_string[y], "%%%02X", (unsigned int)(input[x] & 0xFF));
2590 			y += 3;
2591 			}
2592 		}
2593 
2594 	/* terminate encoded string */
2595 	encoded_url_string[y] = '\x0';
2596 
2597 	return encoded_url_string;
2598 	}
2599 
2600 
2601 static int macro_key_cmp(const void *a_, const void *b_)
2602 {
2603 	struct macro_key_code *a = (struct macro_key_code *)a_;
2604 	struct macro_key_code *b = (struct macro_key_code *)b_;
2605 
2606 	return strcmp(a->name, b->name);
2607 }
2608 
2609 
2610 /******************************************************************/
2611 /***************** MACRO INITIALIZATION FUNCTIONS *****************/
2612 /******************************************************************/
2613 
2614 /* initializes global macros */
2615 int init_macros(void) {
2616 	int x;
2617 	init_macrox_names();
2618 
2619 	for(x = 0; x < 32; x++)
2620 		illegal_output_char_map[x] = 1;
2621 	illegal_output_char_map[127] = 1;
2622 
2623 	/*
2624 	 * non-volatile macros are free()'d when they're set.
2625 	 * We must do this in order to not lose the constant
2626 	 * ones when we get SIGHUP or a RESTART_PROGRAM event
2627 	 * from the command fifo. Otherwise a memset() would
2628 	 * have been better.
2629 	 */
2630 	clear_volatile_macros_r(&global_macros);
2631 
2632 	/* backwards compatibility hack */
2633 	macro_x = global_macros.x;
2634 
2635 	/*
2636 	 * Now build an ordered list of X macro names so we can
2637 	 * do binary lookups later and avoid a ton of strcmp()'s
2638 	 * for each and every check that gets run. A hash table
2639 	 * is actually slower, since the most frequently used
2640 	 * keys are so long and a binary lookup is completed in
2641 	 * 7 steps for up to ~200 keys, worst case.
2642 	 */
2643 	for (x = 0; x < MACRO_X_COUNT; x++) {
2644 		macro_keys[x].code = x;
2645 		macro_keys[x].name = macro_x_names[x];
2646 
2647 		/* This tells which escaping is possible to do on the macro */
2648 		macro_keys[x].options = URL_ENCODE_MACRO_CHARS;
2649 		switch(x) {
2650 		case MACRO_HOSTOUTPUT:
2651 		case MACRO_LONGHOSTOUTPUT:
2652 		case MACRO_HOSTPERFDATA:
2653 		case MACRO_HOSTACKAUTHOR:
2654 		case MACRO_HOSTACKCOMMENT:
2655 		case MACRO_SERVICEOUTPUT:
2656 		case MACRO_LONGSERVICEOUTPUT:
2657 		case MACRO_SERVICEPERFDATA:
2658 		case MACRO_SERVICEACKAUTHOR:
2659 		case MACRO_SERVICEACKCOMMENT:
2660 			macro_keys[x].options |= STRIP_ILLEGAL_MACRO_CHARS | ESCAPE_MACRO_CHARS;
2661 			break;
2662 		}
2663 	}
2664 
2665 	qsort(macro_keys, x, sizeof(struct macro_key_code), macro_key_cmp);
2666 	return OK;
2667 	}
2668 
2669 /*
2670  * initializes the names of macros, using this nifty little macro
2671  * which ensures we never add any typos to the list
2672  */
2673 #define add_macrox_name(name) macro_x_names[MACRO_##name] = strdup(#name)
2674 int init_macrox_names(void) {
2675 	register int x = 0;
2676 
2677 	/* initialize macro names */
2678 	for(x = 0; x < MACRO_X_COUNT; x++)
2679 		macro_x_names[x] = NULL;
2680 
2681 	/* initialize each macro name */
2682 	add_macrox_name(HOSTNAME);
2683 	add_macrox_name(HOSTALIAS);
2684 	add_macrox_name(HOSTADDRESS);
2685 	add_macrox_name(SERVICEDESC);
2686 	add_macrox_name(SERVICESTATE);
2687 	add_macrox_name(SERVICESTATEID);
2688 	add_macrox_name(SERVICEATTEMPT);
2689 	add_macrox_name(SERVICEISVOLATILE);
2690 	add_macrox_name(LONGDATETIME);
2691 	add_macrox_name(SHORTDATETIME);
2692 	add_macrox_name(DATE);
2693 	add_macrox_name(TIME);
2694 	add_macrox_name(TIMET);
2695 	add_macrox_name(LASTHOSTCHECK);
2696 	add_macrox_name(LASTSERVICECHECK);
2697 	add_macrox_name(LASTHOSTSTATECHANGE);
2698 	add_macrox_name(LASTSERVICESTATECHANGE);
2699 	add_macrox_name(HOSTOUTPUT);
2700 	add_macrox_name(SERVICEOUTPUT);
2701 	add_macrox_name(HOSTPERFDATA);
2702 	add_macrox_name(SERVICEPERFDATA);
2703 	add_macrox_name(CONTACTNAME);
2704 	add_macrox_name(CONTACTALIAS);
2705 	add_macrox_name(CONTACTEMAIL);
2706 	add_macrox_name(CONTACTPAGER);
2707 	add_macrox_name(ADMINEMAIL);
2708 	add_macrox_name(ADMINPAGER);
2709 	add_macrox_name(HOSTSTATE);
2710 	add_macrox_name(HOSTSTATEID);
2711 	add_macrox_name(HOSTATTEMPT);
2712 	add_macrox_name(NOTIFICATIONTYPE);
2713 	add_macrox_name(NOTIFICATIONNUMBER);
2714 	add_macrox_name(NOTIFICATIONISESCALATED);
2715 	add_macrox_name(HOSTEXECUTIONTIME);
2716 	add_macrox_name(SERVICEEXECUTIONTIME);
2717 	add_macrox_name(HOSTLATENCY);
2718 	add_macrox_name(SERVICELATENCY);
2719 	add_macrox_name(HOSTDURATION);
2720 	add_macrox_name(SERVICEDURATION);
2721 	add_macrox_name(HOSTDURATIONSEC);
2722 	add_macrox_name(SERVICEDURATIONSEC);
2723 	add_macrox_name(HOSTDOWNTIME);
2724 	add_macrox_name(SERVICEDOWNTIME);
2725 	add_macrox_name(HOSTSTATETYPE);
2726 	add_macrox_name(SERVICESTATETYPE);
2727 	add_macrox_name(HOSTPERCENTCHANGE);
2728 	add_macrox_name(SERVICEPERCENTCHANGE);
2729 	add_macrox_name(HOSTGROUPNAME);
2730 	add_macrox_name(HOSTGROUPALIAS);
2731 	add_macrox_name(SERVICEGROUPNAME);
2732 	add_macrox_name(SERVICEGROUPALIAS);
2733 	add_macrox_name(HOSTACKAUTHOR);
2734 	add_macrox_name(HOSTACKCOMMENT);
2735 	add_macrox_name(SERVICEACKAUTHOR);
2736 	add_macrox_name(SERVICEACKCOMMENT);
2737 	add_macrox_name(LASTSERVICEOK);
2738 	add_macrox_name(LASTSERVICEWARNING);
2739 	add_macrox_name(LASTSERVICEUNKNOWN);
2740 	add_macrox_name(LASTSERVICECRITICAL);
2741 	add_macrox_name(LASTHOSTUP);
2742 	add_macrox_name(LASTHOSTDOWN);
2743 	add_macrox_name(LASTHOSTUNREACHABLE);
2744 	add_macrox_name(SERVICECHECKCOMMAND);
2745 	add_macrox_name(HOSTCHECKCOMMAND);
2746 	add_macrox_name(MAINCONFIGFILE);
2747 	add_macrox_name(STATUSDATAFILE);
2748 	add_macrox_name(HOSTDISPLAYNAME);
2749 	add_macrox_name(SERVICEDISPLAYNAME);
2750 	add_macrox_name(RETENTIONDATAFILE);
2751 	add_macrox_name(OBJECTCACHEFILE);
2752 	add_macrox_name(TEMPFILE);
2753 	add_macrox_name(LOGFILE);
2754 	add_macrox_name(RESOURCEFILE);
2755 	add_macrox_name(COMMANDFILE);
2756 	add_macrox_name(HOSTPERFDATAFILE);
2757 	add_macrox_name(SERVICEPERFDATAFILE);
2758 	add_macrox_name(HOSTACTIONURL);
2759 	add_macrox_name(HOSTNOTESURL);
2760 	add_macrox_name(HOSTNOTES);
2761 	add_macrox_name(SERVICEACTIONURL);
2762 	add_macrox_name(SERVICENOTESURL);
2763 	add_macrox_name(SERVICENOTES);
2764 	add_macrox_name(TOTALHOSTSUP);
2765 	add_macrox_name(TOTALHOSTSDOWN);
2766 	add_macrox_name(TOTALHOSTSUNREACHABLE);
2767 	add_macrox_name(TOTALHOSTSDOWNUNHANDLED);
2768 	add_macrox_name(TOTALHOSTSUNREACHABLEUNHANDLED);
2769 	add_macrox_name(TOTALHOSTPROBLEMS);
2770 	add_macrox_name(TOTALHOSTPROBLEMSUNHANDLED);
2771 	add_macrox_name(TOTALSERVICESOK);
2772 	add_macrox_name(TOTALSERVICESWARNING);
2773 	add_macrox_name(TOTALSERVICESCRITICAL);
2774 	add_macrox_name(TOTALSERVICESUNKNOWN);
2775 	add_macrox_name(TOTALSERVICESWARNINGUNHANDLED);
2776 	add_macrox_name(TOTALSERVICESCRITICALUNHANDLED);
2777 	add_macrox_name(TOTALSERVICESUNKNOWNUNHANDLED);
2778 	add_macrox_name(TOTALSERVICEPROBLEMS);
2779 	add_macrox_name(TOTALSERVICEPROBLEMSUNHANDLED);
2780 	add_macrox_name(PROCESSSTARTTIME);
2781 	add_macrox_name(HOSTCHECKTYPE);
2782 	add_macrox_name(SERVICECHECKTYPE);
2783 	add_macrox_name(LONGHOSTOUTPUT);
2784 	add_macrox_name(LONGSERVICEOUTPUT);
2785 	add_macrox_name(TEMPPATH);
2786 	add_macrox_name(HOSTNOTIFICATIONNUMBER);
2787 	add_macrox_name(SERVICENOTIFICATIONNUMBER);
2788 	add_macrox_name(HOSTNOTIFICATIONID);
2789 	add_macrox_name(SERVICENOTIFICATIONID);
2790 	add_macrox_name(HOSTEVENTID);
2791 	add_macrox_name(LASTHOSTEVENTID);
2792 	add_macrox_name(SERVICEEVENTID);
2793 	add_macrox_name(LASTSERVICEEVENTID);
2794 	add_macrox_name(HOSTGROUPNAMES);
2795 	add_macrox_name(SERVICEGROUPNAMES);
2796 	add_macrox_name(HOSTACKAUTHORNAME);
2797 	add_macrox_name(HOSTACKAUTHORALIAS);
2798 	add_macrox_name(SERVICEACKAUTHORNAME);
2799 	add_macrox_name(SERVICEACKAUTHORALIAS);
2800 	add_macrox_name(MAXHOSTATTEMPTS);
2801 	add_macrox_name(MAXSERVICEATTEMPTS);
2802 	add_macrox_name(TOTALHOSTSERVICES);
2803 	add_macrox_name(TOTALHOSTSERVICESOK);
2804 	add_macrox_name(TOTALHOSTSERVICESWARNING);
2805 	add_macrox_name(TOTALHOSTSERVICESUNKNOWN);
2806 	add_macrox_name(TOTALHOSTSERVICESCRITICAL);
2807 	add_macrox_name(HOSTGROUPNOTES);
2808 	add_macrox_name(HOSTGROUPNOTESURL);
2809 	add_macrox_name(HOSTGROUPACTIONURL);
2810 	add_macrox_name(SERVICEGROUPNOTES);
2811 	add_macrox_name(SERVICEGROUPNOTESURL);
2812 	add_macrox_name(SERVICEGROUPACTIONURL);
2813 	add_macrox_name(HOSTGROUPMEMBERS);
2814 	add_macrox_name(SERVICEGROUPMEMBERS);
2815 	add_macrox_name(CONTACTGROUPNAME);
2816 	add_macrox_name(CONTACTGROUPALIAS);
2817 	add_macrox_name(CONTACTGROUPMEMBERS);
2818 	add_macrox_name(CONTACTGROUPNAMES);
2819 	add_macrox_name(NOTIFICATIONRECIPIENTS);
2820 	add_macrox_name(NOTIFICATIONAUTHOR);
2821 	add_macrox_name(NOTIFICATIONAUTHORNAME);
2822 	add_macrox_name(NOTIFICATIONAUTHORALIAS);
2823 	add_macrox_name(NOTIFICATIONCOMMENT);
2824 	add_macrox_name(EVENTSTARTTIME);
2825 	add_macrox_name(HOSTPROBLEMID);
2826 	add_macrox_name(LASTHOSTPROBLEMID);
2827 	add_macrox_name(SERVICEPROBLEMID);
2828 	add_macrox_name(LASTSERVICEPROBLEMID);
2829 	add_macrox_name(ISVALIDTIME);
2830 	add_macrox_name(NEXTVALIDTIME);
2831 	add_macrox_name(LASTHOSTSTATE);
2832 	add_macrox_name(LASTHOSTSTATEID);
2833 	add_macrox_name(LASTSERVICESTATE);
2834 	add_macrox_name(LASTSERVICESTATEID);
2835 	add_macrox_name(HOSTIMPORTANCE);
2836 	add_macrox_name(SERVICEIMPORTANCE);
2837 	add_macrox_name(HOSTANDSERVICESIMPORTANCE);
2838 	add_macrox_name(HOSTGROUPMEMBERADDRESSES);
2839 	add_macrox_name(HOSTINFOURL);
2840 	add_macrox_name(SERVICEINFOURL);
2841 	add_macrox_name(HOSTNOTIFICATIONENABLED);
2842 	add_macrox_name(SERVICENOTIFICATIONENABLED);
2843 	add_macrox_name(HOSTNOTIFICATIONPERIOD);
2844 	add_macrox_name(SERVICENOTIFICATIONPERIOD);
2845 
2846 	return OK;
2847 	}
2848 
2849 
2850 /******************************************************************/
2851 /********************* MACRO CLEANUP FUNCTIONS ********************/
2852 /******************************************************************/
2853 
2854 /* free memory associated with the macrox names */
2855 int free_macrox_names(void) {
2856 	register int x = 0;
2857 
2858 	/* free each macro name */
2859 	for(x = 0; x < MACRO_X_COUNT; x++)
2860 		my_free(macro_x_names[x]);
2861 
2862 	return OK;
2863 	}
2864 
2865 
2866 
2867 /* clear argv macros - used in commands */
2868 int clear_argv_macros_r(nagios_macros *mac) {
2869 	register int x = 0;
2870 
2871 	/* command argument macros */
2872 	for(x = 0; x < MAX_COMMAND_ARGUMENTS; x++)
2873 		my_free(mac->argv[x]);
2874 
2875 	return OK;
2876 	}
2877 
2878 int clear_argv_macros(void) {
2879 	return clear_argv_macros_r(&global_macros);
2880 	}
2881 
2882 /*
2883  * copies non-volatile macros from global macro_x to **dest, which
2884  * must be large enough to hold at least MACRO_X_COUNT entries.
2885  * We use a shortlived macro to save up on typing
2886  */
2887 #define cp_macro(name) dest[MACRO_##name] = global_macros.x[MACRO_##name]
2888 void copy_constant_macros(char **dest) {
2889 	cp_macro(ADMINEMAIL);
2890 	cp_macro(ADMINPAGER);
2891 	cp_macro(MAINCONFIGFILE);
2892 	cp_macro(STATUSDATAFILE);
2893 	cp_macro(RETENTIONDATAFILE);
2894 	cp_macro(OBJECTCACHEFILE);
2895 	cp_macro(TEMPFILE);
2896 	cp_macro(LOGFILE);
2897 	cp_macro(RESOURCEFILE);
2898 	cp_macro(COMMANDFILE);
2899 	cp_macro(HOSTPERFDATAFILE);
2900 	cp_macro(SERVICEPERFDATAFILE);
2901 	cp_macro(PROCESSSTARTTIME);
2902 	cp_macro(TEMPPATH);
2903 	cp_macro(EVENTSTARTTIME);
2904 	}
2905 #undef cp_macro
2906 
2907 static void clear_custom_vars(customvariablesmember **vars) {
2908 	customvariablesmember *this_customvariablesmember = NULL;
2909 	customvariablesmember *next_customvariablesmember = NULL;
2910 
2911 	for(this_customvariablesmember = *vars;
2912 			this_customvariablesmember != NULL;
2913 			this_customvariablesmember = next_customvariablesmember) {
2914 		next_customvariablesmember = this_customvariablesmember->next;
2915 		my_free(this_customvariablesmember->variable_name);
2916 		my_free(this_customvariablesmember->variable_value);
2917 		my_free(this_customvariablesmember);
2918 		}
2919 	*vars = NULL;
2920 	}
2921 
2922 /* clear all macros that are not "constant" (i.e. they change throughout the course of monitoring) */
2923 int clear_volatile_macros_r(nagios_macros *mac) {
2924 	register int x = 0;
2925 
2926 	log_debug_info(DEBUGL_FUNCTIONS, 0, "clear_volatile_macros_r()\n");
2927 
2928 	clear_host_macros_r(mac);
2929 	clear_service_macros_r(mac);
2930 	clear_summary_macros_r(mac);
2931 	clear_hostgroup_macros_r(mac);
2932 	clear_servicegroup_macros_r(mac);
2933 	clear_contactgroup_macros_r(mac);
2934 	clear_contact_macros_r(mac);
2935 	clear_datetime_macros_r(mac);
2936 
2937 	/* contact address macros */
2938 	for(x = 0; x < MAX_CONTACT_ADDRESSES; x++)
2939 		my_free(mac->contactaddress[x]);
2940 
2941 	/* clear on-demand macro */
2942 	my_free(mac->ondemand);
2943 
2944 	/* clear ARGx macros */
2945 	clear_argv_macros_r(mac);
2946 
2947 	return OK;
2948 	}
2949 
2950 
2951 int clear_volatile_macros(void) {
2952 	return clear_volatile_macros_r(&global_macros);
2953 	}
2954 
2955 
2956 /* clear datetime macros */
2957 int clear_datetime_macros_r(nagios_macros *mac) {
2958 	my_free(mac->x[MACRO_LONGDATETIME]);
2959 	my_free(mac->x[MACRO_SHORTDATETIME]);
2960 	my_free(mac->x[MACRO_DATE]);
2961 	my_free(mac->x[MACRO_TIME]);
2962 
2963 	return OK;
2964 	}
2965 
2966 /* clear service macros */
2967 int clear_service_macros_r(nagios_macros *mac) {
2968 
2969 	/* these are recursive but persistent. what to do? */
2970 	my_free(mac->x[MACRO_SERVICEACTIONURL]);
2971 	my_free(mac->x[MACRO_SERVICENOTESURL]);
2972 	my_free(mac->x[MACRO_SERVICENOTES]);
2973 
2974 	my_free(mac->x[MACRO_SERVICEGROUPNAMES]);
2975 	my_free(mac->x[MACRO_SERVICEINFOURL]);
2976 
2977 	/* clear custom service variables */
2978 	clear_custom_vars(&(mac->custom_service_vars));
2979 
2980 	/* clear pointers */
2981 	mac->service_ptr = NULL;
2982 
2983 	return OK;
2984 	}
2985 
2986 int clear_service_macros(void) {
2987 	return clear_service_macros_r(&global_macros);
2988 	}
2989 
2990 /* clear host macros */
2991 int clear_host_macros_r(nagios_macros *mac) {
2992 
2993 	/* these are recursive but persistent. what to do? */
2994 	my_free(mac->x[MACRO_HOSTACTIONURL]);
2995 	my_free(mac->x[MACRO_HOSTNOTESURL]);
2996 	my_free(mac->x[MACRO_HOSTNOTES]);
2997 
2998 	/* numbers or by necessity autogenerated strings */
2999 	my_free(mac->x[MACRO_HOSTGROUPNAMES]);
3000 	my_free(mac->x[MACRO_HOSTINFOURL]);
3001 
3002 	/* clear custom host variables */
3003 	clear_custom_vars(&(mac->custom_host_vars));
3004 
3005 	/* clear pointers */
3006 	mac->host_ptr = NULL;
3007 
3008 	return OK;
3009 	}
3010 
3011 int clear_host_macros(void) {
3012 	return clear_host_macros_r(&global_macros);
3013 	}
3014 
3015 
3016 /* clear hostgroup macros */
3017 int clear_hostgroup_macros_r(nagios_macros *mac) {
3018 
3019 	/* recursive but persistent. what to do? */
3020 	my_free(mac->x[MACRO_HOSTGROUPACTIONURL]);
3021 	my_free(mac->x[MACRO_HOSTGROUPNOTESURL]);
3022 	my_free(mac->x[MACRO_HOSTGROUPNOTES]);
3023 
3024 	/* generated */
3025 	my_free(mac->x[MACRO_HOSTGROUPMEMBERS]);
3026 	my_free(mac->x[MACRO_HOSTGROUPMEMBERADDRESSES]);
3027 
3028 	/* clear pointers */
3029 	mac->hostgroup_ptr = NULL;
3030 
3031 	return OK;
3032 	}
3033 
3034 int clear_hostgroup_macros(void) {
3035 	return clear_hostgroup_macros_r(&global_macros);
3036 	}
3037 
3038 
3039 /* clear servicegroup macros */
3040 int clear_servicegroup_macros_r(nagios_macros *mac) {
3041 	/* recursive but persistent. what to do? */
3042 	my_free(mac->x[MACRO_SERVICEGROUPACTIONURL]);
3043 	my_free(mac->x[MACRO_SERVICEGROUPNOTESURL]);
3044 	my_free(mac->x[MACRO_SERVICEGROUPNOTES]);
3045 
3046 	/* generated */
3047 	my_free(mac->x[MACRO_SERVICEGROUPMEMBERS]);
3048 
3049 	/* clear pointers */
3050 	mac->servicegroup_ptr = NULL;
3051 
3052 	return OK;
3053 	}
3054 
3055 int clear_servicegroup_macros(void) {
3056 	return clear_servicegroup_macros_r(&global_macros);
3057 	}
3058 
3059 
3060 /* clear contact macros */
3061 int clear_contact_macros_r(nagios_macros *mac) {
3062 
3063 	/* generated */
3064 	my_free(mac->x[MACRO_CONTACTGROUPNAMES]);
3065 
3066 	/* clear custom contact variables */
3067 	clear_custom_vars(&(mac->custom_contact_vars));
3068 
3069 	/* clear pointers */
3070 	mac->contact_ptr = NULL;
3071 
3072 	return OK;
3073 	}
3074 
3075 int clear_contact_macros(void) {
3076 	return clear_contact_macros_r(&global_macros);
3077 	}
3078 
3079 
3080 /* clear contactgroup macros */
3081 int clear_contactgroup_macros_r(nagios_macros *mac) {
3082 	/* generated */
3083 	my_free(mac->x[MACRO_CONTACTGROUPMEMBERS]);
3084 
3085 	/* clear pointers */
3086 	mac->contactgroup_ptr = NULL;
3087 
3088 	return OK;
3089 	}
3090 
3091 int clear_contactgroup_macros(void) {
3092 	return clear_contactgroup_macros_r(&global_macros);
3093 	}
3094 
3095 /* clear summary macros */
3096 int clear_summary_macros_r(nagios_macros *mac) {
3097 	register int x;
3098 
3099 	for(x = MACRO_TOTALHOSTSUP; x <= MACRO_TOTALSERVICEPROBLEMSUNHANDLED; x++)
3100 		my_free(mac->x[x]);
3101 
3102 	return OK;
3103 	}
3104 
3105 int clear_summary_macros(void) {
3106 	return clear_summary_macros_r(&global_macros);
3107 	}
3108 
3109 
3110 /******************************************************************/
3111 /****************** ENVIRONMENT MACRO FUNCTIONS *******************/
3112 /******************************************************************/
3113 
3114 #ifdef NSCORE
3115 
3116 /* sets or unsets all macro environment variables */
3117 int set_all_macro_environment_vars_r(nagios_macros *mac, int set) {
3118 	if(enable_environment_macros == FALSE)
3119 		return ERROR;
3120 
3121 	set_macrox_environment_vars_r(mac, set);
3122 	set_argv_macro_environment_vars_r(mac, set);
3123 	set_custom_macro_environment_vars_r(mac, set);
3124 	set_contact_address_environment_vars_r(mac, set);
3125 
3126 	return OK;
3127 	}
3128 
3129 int set_all_macro_environment_vars(int set) {
3130 	return set_all_macro_environment_vars_r(&global_macros, set);
3131 	}
3132 
3133 
3134 /* sets or unsets macrox environment variables */
3135 int set_macrox_environment_vars_r(nagios_macros *mac, int set) {
3136 	register int x = 0;
3137 	int free_macro = FALSE;
3138 
3139 	/* set each of the macrox environment variables */
3140 	for(x = 0; x < MACRO_X_COUNT; x++) {
3141 
3142 		free_macro = FALSE;
3143 
3144 		/* large installations don't get all macros */
3145 		if(use_large_installation_tweaks == TRUE) {
3146 			/*
3147 			 * member macros tend to overflow the
3148 			 * environment on large installations
3149 			 */
3150 			if(x == MACRO_SERVICEGROUPMEMBERS || x == MACRO_HOSTGROUPMEMBERS || x == MACRO_HOSTGROUPMEMBERADDRESSES)
3151 				continue;
3152 
3153 			/* summary macros are CPU intensive to compute */
3154 			if(x >= MACRO_TOTALHOSTSUP && x <= MACRO_TOTALSERVICEPROBLEMSUNHANDLED)
3155 				continue;
3156 			}
3157 
3158 		/* generate the macro value if it hasn't already been done */
3159 		/* THIS IS EXPENSIVE */
3160 		if(set == TRUE) {
3161 
3162 			if(mac->x[x] == NULL)
3163 				grab_macrox_value_r(mac, x, NULL, NULL, &mac->x[x], &free_macro);
3164 			}
3165 
3166 		/* set the value */
3167 		set_macro_environment_var(macro_x_names[x], mac->x[x], set);
3168 		}
3169 
3170 	return OK;
3171 	}
3172 
3173 int set_macrox_environment_vars(int set) {
3174 	return set_macrox_environment_vars_r(&global_macros, set);
3175 	}
3176 
3177 
3178 /* sets or unsets argv macro environment variables */
3179 int set_argv_macro_environment_vars_r(nagios_macros *mac, int set) {
3180 	char *macro_name = NULL;
3181 	register int x = 0;
3182 
3183 	/* set each of the argv macro environment variables */
3184 	for(x = 0; x < MAX_COMMAND_ARGUMENTS; x++) {
3185 		asprintf(&macro_name, "ARG%d", x + 1);
3186 		set_macro_environment_var(macro_name, mac->argv[x], set);
3187 		my_free(macro_name);
3188 		}
3189 
3190 	return OK;
3191 	}
3192 
3193 int set_argv_macro_environment_vars(int set) {
3194 	return set_argv_macro_environment_vars_r(&global_macros, set);
3195 	}
3196 
3197 
3198 /* sets or unsets custom host/service/contact macro environment variables */
3199 int set_custom_macro_environment_vars_r(nagios_macros *mac, int set) {
3200 	customvariablesmember *temp_customvariablesmember = NULL;
3201 	host *temp_host = NULL;
3202 	service *temp_service = NULL;
3203 	contact *temp_contact = NULL;
3204 	char *customvarname = NULL;
3205 
3206 	/***** CUSTOM HOST VARIABLES *****/
3207 	/* generate variables and save them for later */
3208 	if((temp_host = mac->host_ptr) && set == TRUE) {
3209 		for(temp_customvariablesmember = temp_host->custom_variables; temp_customvariablesmember != NULL; temp_customvariablesmember = temp_customvariablesmember->next) {
3210 			asprintf(&customvarname, "_HOST%s", temp_customvariablesmember->variable_name);
3211 			add_custom_variable_to_object(&mac->custom_host_vars, customvarname, temp_customvariablesmember->variable_value);
3212 			my_free(customvarname);
3213 			}
3214 		}
3215 	/* set variables */
3216 	for(temp_customvariablesmember = mac->custom_host_vars; temp_customvariablesmember != NULL; temp_customvariablesmember = temp_customvariablesmember->next) {
3217 		set_macro_environment_var(temp_customvariablesmember->variable_name, clean_macro_chars(temp_customvariablesmember->variable_value, STRIP_ILLEGAL_MACRO_CHARS | ESCAPE_MACRO_CHARS), set);
3218 		}
3219 
3220 	/***** CUSTOM SERVICE VARIABLES *****/
3221 	/* generate variables and save them for later */
3222 	if((temp_service = mac->service_ptr) && set == TRUE) {
3223 		for(temp_customvariablesmember = temp_service->custom_variables; temp_customvariablesmember != NULL; temp_customvariablesmember = temp_customvariablesmember->next) {
3224 			asprintf(&customvarname, "_SERVICE%s", temp_customvariablesmember->variable_name);
3225 			add_custom_variable_to_object(&mac->custom_service_vars, customvarname, temp_customvariablesmember->variable_value);
3226 			my_free(customvarname);
3227 			}
3228 		}
3229 	/* set variables */
3230 	for(temp_customvariablesmember = mac->custom_service_vars; temp_customvariablesmember != NULL; temp_customvariablesmember = temp_customvariablesmember->next)
3231 		set_macro_environment_var(temp_customvariablesmember->variable_name, clean_macro_chars(temp_customvariablesmember->variable_value, STRIP_ILLEGAL_MACRO_CHARS | ESCAPE_MACRO_CHARS), set);
3232 
3233 	/***** CUSTOM CONTACT VARIABLES *****/
3234 	/* generate variables and save them for later */
3235 	if((temp_contact = mac->contact_ptr) && set == TRUE) {
3236 		for(temp_customvariablesmember = temp_contact->custom_variables; temp_customvariablesmember != NULL; temp_customvariablesmember = temp_customvariablesmember->next) {
3237 			asprintf(&customvarname, "_CONTACT%s", temp_customvariablesmember->variable_name);
3238 			add_custom_variable_to_object(&mac->custom_contact_vars, customvarname, temp_customvariablesmember->variable_value);
3239 			my_free(customvarname);
3240 			}
3241 		}
3242 	/* set variables */
3243 	for(temp_customvariablesmember = mac->custom_contact_vars; temp_customvariablesmember != NULL; temp_customvariablesmember = temp_customvariablesmember->next)
3244 		set_macro_environment_var(temp_customvariablesmember->variable_name, clean_macro_chars(temp_customvariablesmember->variable_value, STRIP_ILLEGAL_MACRO_CHARS | ESCAPE_MACRO_CHARS), set);
3245 
3246 	return OK;
3247 	}
3248 
3249 int set_custom_macro_environment_vars(int set) {
3250 	return set_custom_macro_environment_vars_r(&global_macros, set);
3251 	}
3252 
3253 
3254 /* sets or unsets contact address environment variables */
3255 int set_contact_address_environment_vars_r(nagios_macros *mac, int set) {
3256 	char *varname = NULL;
3257 	register int x;
3258 
3259 	/* these only get set during notifications */
3260 	if(mac->contact_ptr == NULL)
3261 		return OK;
3262 
3263 	for(x = 0; x < MAX_CONTACT_ADDRESSES; x++) {
3264 		asprintf(&varname, "CONTACTADDRESS%d", x);
3265 		set_macro_environment_var(varname, mac->contact_ptr->address[x], set);
3266 		my_free(varname);
3267 		}
3268 
3269 	return OK;
3270 	}
3271 
3272 int set_contact_address_environment_vars(int set) {
3273 	return set_contact_address_environment_vars_r(&global_macros, set);
3274 	}
3275 
3276 
3277 /* sets or unsets a macro environment variable */
3278 int set_macro_environment_var(char *name, char *value, int set) {
3279 	char *env_macro_name = NULL;
3280 
3281 	/* we won't mess with null variable names */
3282 	if(name == NULL)
3283 		return ERROR;
3284 
3285 	/* create environment var name */
3286 	asprintf(&env_macro_name, "%s%s", MACRO_ENV_VAR_PREFIX, name);
3287 
3288 	/* set or unset the environment variable */
3289 	set_environment_var(env_macro_name, value, set);
3290 
3291 	/* free allocated memory */
3292 	my_free(env_macro_name);
3293 
3294 	return OK;
3295 	}
3296 
3297 static int add_macrox_environment_vars_r(nagios_macros *, struct kvvec *);
3298 static int add_argv_macro_environment_vars_r(nagios_macros *, struct kvvec *);
3299 static int add_custom_macro_environment_vars_r(nagios_macros *, struct kvvec *);
3300 static int add_contact_address_environment_vars_r(nagios_macros *,
3301 		struct kvvec *);
3302 
3303 struct kvvec * macros_to_kvv(nagios_macros *mac) {
3304 
3305 	struct kvvec *kvvp;
3306 
3307 	log_debug_info(DEBUGL_FUNCTIONS, 0, "macros_to_kvv()\n");
3308 
3309 	/* If we're not supposed to export macros as environment variables,
3310 		just return */
3311 	if(FALSE == enable_environment_macros) return NULL;
3312 
3313 	/* Create the kvvec to hold the macros */
3314 	if((kvvp = calloc(1, sizeof(struct kvvec))) == NULL) return NULL;
3315 	if(!kvvec_init(kvvp, MACRO_X_COUNT + MAX_COMMAND_ARGUMENTS + MAX_CONTACT_ADDRESSES + 4)) return NULL;
3316 
3317 	add_macrox_environment_vars_r(mac, kvvp);
3318 	add_argv_macro_environment_vars_r(mac, kvvp);
3319 	add_custom_macro_environment_vars_r(mac, kvvp);
3320 	add_contact_address_environment_vars_r(mac, kvvp);
3321 
3322 	return kvvp;
3323 	}
3324 
3325 /* adds macrox environment variables */
3326 static int add_macrox_environment_vars_r(nagios_macros *mac, struct kvvec *kvvp)
3327 {
3328 	/*register*/ int x = 0;
3329 	int free_macro = FALSE;
3330 	char *envname;
3331 
3332 	log_debug_info(DEBUGL_FUNCTIONS, 1, "add_macrox_environment_vars_r()\n");
3333 
3334 	/* set each of the macrox environment variables */
3335 	for(x = 0; x < MACRO_X_COUNT; x++) {
3336 
3337 		log_debug_info(DEBUGL_MACROS, 2, "Processing macro %d of %d\n", x,
3338 				MACRO_X_COUNT);
3339 		free_macro = FALSE;
3340 
3341 		/* large installations don't get all macros */
3342 		if(use_large_installation_tweaks == TRUE) {
3343 			/*
3344 			 * member macros tend to overflow the
3345 			 * environment on large installations
3346 			 */
3347 			if(x == MACRO_SERVICEGROUPMEMBERS || x == MACRO_HOSTGROUPMEMBERS ||
3348 				x == MACRO_HOSTGROUPMEMBERADDRESSES)
3349 				continue;
3350 
3351 			/* summary macros are CPU intensive to compute */
3352 			if(x >= MACRO_TOTALHOSTSUP && x <= MACRO_TOTALSERVICEPROBLEMSUNHANDLED)
3353 				continue;
3354 			}
3355 
3356 		/* generate the macro value if it hasn't already been done */
3357 		/* THIS IS EXPENSIVE */
3358 		if(mac->x[x] == NULL) {
3359 			log_debug_info(DEBUGL_MACROS, 2, "Grabbing value for macro: %s\n",
3360 					macro_x_names[x]);
3361 
3362 			grab_macrox_value_r(mac, x, NULL, NULL, &mac->x[x], &free_macro);
3363 			}
3364 
3365 		/* add the value to the kvvec */
3366 		log_debug_info(DEBUGL_MACROS, 2, "Adding macro \"%s\" with value \"%s\" to kvvec\n",
3367 					macro_x_names[x], mac->x[x]);
3368 		/* Allocate memory for each environment variable name, but not the
3369 			values because when kvvec_destroy() is called, it is called with
3370 			KVVEC_FREE_KEYS */
3371 		asprintf(&envname, "%s%s", MACRO_ENV_VAR_PREFIX, macro_x_names[x]);
3372 		kvvec_addkv(kvvp, envname, mac->x[x]);
3373 		}
3374 
3375 	log_debug_info(DEBUGL_FUNCTIONS, 2, "add_macrox_environment_vars_r() end\n");
3376 	return OK;
3377 	}
3378 
3379 /* adds argv macro environment variables */
3380 static int add_argv_macro_environment_vars_r(nagios_macros *mac,
3381 		struct kvvec *kvvp) {
3382 	char *macro_name = NULL;
3383 	register int x = 0;
3384 
3385 	log_debug_info(DEBUGL_FUNCTIONS, 1, "add_argv_macro_environment_vars_r()\n");
3386 
3387 	/* set each of the argv macro environment variables */
3388 	for(x = 0; x < MAX_COMMAND_ARGUMENTS; x++) {
3389 		/* Allocate memory for each environment variable name, but not the
3390 			values because when kvvec_destroy() is called, it is called with
3391 			KVVEC_FREE_KEYS */
3392 		asprintf(&macro_name, "%sARG%d", MACRO_ENV_VAR_PREFIX, x + 1);
3393 		kvvec_addkv(kvvp, macro_name, mac->argv[x]);
3394 		}
3395 
3396 	return OK;
3397 	}
3398 
3399 /* adds custom host/service/contact macro environment variables */
3400 static int add_custom_macro_environment_vars_r(nagios_macros *mac,
3401 		struct kvvec *kvvp) {
3402 
3403 	customvariablesmember *temp_customvariablesmember = NULL;
3404 	host *temp_host = NULL;
3405 	service *temp_service = NULL;
3406 	contact *temp_contact = NULL;
3407 	char *customvarname = NULL;
3408 	char *customvarvalue = NULL;
3409 
3410 	log_debug_info(DEBUGL_FUNCTIONS, 1, "add_custom_macro_environment_vars_r()\n");
3411 
3412 	/***** CUSTOM HOST VARIABLES *****/
3413 	/* generate variables and save them for later */
3414 	temp_host = mac->host_ptr;
3415 	if(temp_host) {
3416 		for(temp_customvariablesmember = temp_host->custom_variables;
3417 				temp_customvariablesmember != NULL;
3418 				temp_customvariablesmember = temp_customvariablesmember->next) {
3419 			asprintf(&customvarname, "%s_HOST%s", MACRO_ENV_VAR_PREFIX,
3420 					temp_customvariablesmember->variable_name);
3421 			add_custom_variable_to_object(&mac->custom_host_vars, customvarname,
3422 					temp_customvariablesmember->variable_value);
3423 			my_free(customvarname);
3424 			}
3425 		}
3426 	/* set variables */
3427 	for(temp_customvariablesmember = mac->custom_host_vars;
3428 			temp_customvariablesmember != NULL;
3429 			temp_customvariablesmember = temp_customvariablesmember->next) {
3430 		customvarvalue =
3431 				clean_macro_chars(temp_customvariablesmember->variable_value,
3432 				STRIP_ILLEGAL_MACRO_CHARS | ESCAPE_MACRO_CHARS);
3433 		if(customvarvalue && *customvarvalue) {
3434 			my_free(temp_customvariablesmember->variable_value);
3435 			temp_customvariablesmember->variable_value = customvarvalue;
3436 			/* Allocate memory for each environment variable name, but not the
3437 				values because when kvvec_destroy() is called, it is called with
3438 				KVVEC_FREE_KEYS */
3439 			kvvec_addkv(kvvp, strdup(temp_customvariablesmember->variable_name),
3440 					customvarvalue);
3441 			}
3442 		}
3443 
3444 	/***** CUSTOM SERVICE VARIABLES *****/
3445 	/* generate variables and save them for later */
3446 	temp_service = mac->service_ptr;
3447 	if(temp_service) {
3448 		for(temp_customvariablesmember = temp_service->custom_variables;
3449 				temp_customvariablesmember != NULL;
3450 				temp_customvariablesmember = temp_customvariablesmember->next) {
3451 			asprintf(&customvarname, "%s_SERVICE%s", MACRO_ENV_VAR_PREFIX,
3452 					temp_customvariablesmember->variable_name);
3453 			add_custom_variable_to_object(&mac->custom_service_vars,
3454 					customvarname, temp_customvariablesmember->variable_value);
3455 			my_free(customvarname);
3456 			}
3457 		}
3458 	/* set variables */
3459 	for(temp_customvariablesmember = mac->custom_service_vars;
3460 			temp_customvariablesmember != NULL;
3461 			temp_customvariablesmember = temp_customvariablesmember->next) {
3462 		customvarvalue =
3463 				clean_macro_chars(temp_customvariablesmember->variable_value,
3464 				STRIP_ILLEGAL_MACRO_CHARS | ESCAPE_MACRO_CHARS);
3465 		if(customvarvalue && *customvarvalue) {
3466 			my_free(temp_customvariablesmember->variable_value);
3467 			temp_customvariablesmember->variable_value = customvarvalue;
3468 			/* Allocate memory for each environment variable name, but not the
3469 				values because when kvvec_destroy() is called, it is called with
3470 				KVVEC_FREE_KEYS */
3471 			kvvec_addkv(kvvp, strdup(temp_customvariablesmember->variable_name),
3472 					customvarvalue);
3473 			}
3474 		}
3475 
3476 	/***** CUSTOM CONTACT VARIABLES *****/
3477 	/* generate variables and save them for later */
3478 	temp_contact = mac->contact_ptr;
3479 	if(temp_contact) {
3480 		for(temp_customvariablesmember = temp_contact->custom_variables;
3481 				temp_customvariablesmember != NULL;
3482 				temp_customvariablesmember = temp_customvariablesmember->next) {
3483 			asprintf(&customvarname, "%s_CONTACT%s", MACRO_ENV_VAR_PREFIX,
3484 					temp_customvariablesmember->variable_name);
3485 			add_custom_variable_to_object(&mac->custom_contact_vars,
3486 					customvarname, temp_customvariablesmember->variable_value);
3487 			my_free(customvarname);
3488 			}
3489 		}
3490 	/* set variables */
3491 	for(temp_customvariablesmember = mac->custom_contact_vars;
3492 			temp_customvariablesmember != NULL;
3493 			temp_customvariablesmember = temp_customvariablesmember->next) {
3494 		customvarvalue =
3495 				clean_macro_chars(temp_customvariablesmember->variable_value,
3496 				STRIP_ILLEGAL_MACRO_CHARS | ESCAPE_MACRO_CHARS);
3497 		if(customvarvalue && *customvarvalue) {
3498 			my_free(temp_customvariablesmember->variable_value);
3499 			temp_customvariablesmember->variable_value = customvarvalue;
3500 			/* Allocate memory for each environment variable name, but not the
3501 				values because when kvvec_destroy() is called, it is called with
3502 				KVVEC_FREE_KEYS */
3503 			kvvec_addkv(kvvp, strdup(temp_customvariablesmember->variable_name),
3504 					customvarvalue);
3505 			}
3506 		}
3507 
3508 	return OK;
3509 	}
3510 
3511 /* add contact address environment variables */
3512 static int add_contact_address_environment_vars_r(nagios_macros *mac,
3513 		struct kvvec *kvvp) {
3514 
3515 	char *varname = NULL;
3516 	register int x;
3517 
3518 	log_debug_info(DEBUGL_FUNCTIONS, 1, "add_contact_address_environment_vars_r()\n");
3519 
3520 	/* these only get set during notifications */
3521 	if(mac->contact_ptr == NULL)
3522 		return OK;
3523 
3524 	asprintf(&varname, "%sCONTACTNAME", MACRO_ENV_VAR_PREFIX);
3525 	kvvec_addkv(kvvp, varname, mac->contact_ptr->name);
3526     asprintf(&varname, "%sCONTACTALIAS", MACRO_ENV_VAR_PREFIX);
3527     kvvec_addkv(kvvp, varname, mac->contact_ptr->alias);
3528     asprintf(&varname, "%sCONTACTEMAIL", MACRO_ENV_VAR_PREFIX);
3529     kvvec_addkv(kvvp, varname, mac->contact_ptr->email);
3530     asprintf(&varname, "%sCONTACTPAGER", MACRO_ENV_VAR_PREFIX);
3531     kvvec_addkv(kvvp, varname, mac->contact_ptr->pager);
3532 
3533 	for(x = 0; x < MAX_CONTACT_ADDRESSES; x++) {
3534 		/* Allocate memory for each environment variable name, but not the
3535 			values because when kvvec_destroy() is called, it is called with
3536 			KVVEC_FREE_KEYS */
3537 		asprintf(&varname, "%sCONTACTADDRESS%d", MACRO_ENV_VAR_PREFIX, x);
3538 		kvvec_addkv(kvvp, varname, mac->contact_ptr->address[x]);
3539 		}
3540 
3541 	return OK;
3542 	}
3543 
3544 #endif
3545