nagios4/cgi/statuswrl.c
2017-05-19 23:37:19 +02:00

1295 lines
36 KiB
C

/*****************************************************************************
*
* STATUSWRL.C - Nagios 3-D (VRML) Network Status View
*
*
* Description:
*
* This CGI will dynamically create a 3-D VRML model of all hosts that are
* being monitored on your network.
*
* License:
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*****************************************************************************/
#include "../include/config.h"
#include "../include/common.h"
#include "../include/objects.h"
#include "../include/statusdata.h"
#include "../include/cgiutils.h"
#include "../include/getcgi.h"
#include "../include/cgiauth.h"
extern char main_config_file[MAX_FILENAME_LENGTH];
extern char url_html_path[MAX_FILENAME_LENGTH];
extern char url_images_path[MAX_FILENAME_LENGTH];
extern char url_logo_images_path[MAX_FILENAME_LENGTH];
extern char *status_file;
extern char *statuswrl_include;
extern int default_statuswrl_layout_method;
#define NAGIOS_VRML_IMAGE "nagiosvrml.png"
#define DEFAULT_NODE_WIDTH 0.5
#define DEFAULT_HORIZONTAL_SPACING 1.0
#define DEFAULT_VERTICAL_SPACING 1.0
/* needed for auto-layout modes */
#define DEFAULT_NODE_HEIGHT 0.5
#define DEFAULT_NODE_HSPACING 1.0
#define DEFAULT_NODE_VSPACING 1.0
#define CIRCULAR_DRAWING_RADIUS 5.0
#define LAYOUT_USER_SUPPLIED 0
#define LAYOUT_COLLAPSED_TREE 2
#define LAYOUT_BALANCED_TREE 3
#define LAYOUT_CIRCULAR 4
void calculate_host_coords(void);
void calculate_world_bounds(void);
void display_world(void);
void write_global_vrml_data(void);
void draw_process_icon(void);
void draw_host(host *);
void draw_host_links(void);
void draw_host_link(host *, double, double, double, double, double, double);
void document_header(void);
int process_cgivars(void);
int number_of_host_layer_members(host *, int);
int max_child_host_layer_members(host *);
int host_child_depth_separation(host *, host *);
int max_child_host_drawing_width(host *);
void calculate_balanced_tree_coords(host *, int, int);
void calculate_circular_coords(void);
void calculate_circular_layer_coords(host *, double, double, int, int);
authdata current_authdata;
float link_radius = 0.016;
float floor_width = 0.0;
float floor_depth = 0.0;
double min_z_coord = 0.0;
double min_x_coord = 0.0;
double min_y_coord = 0.0;
double max_z_coord = 0.0;
double max_x_coord = 0.0;
double max_y_coord = 0.0;
double max_world_size = 0.0;
double nagios_icon_x = 0.0;
double nagios_icon_y = 0.0;
int draw_nagios_icon = FALSE;
double custom_viewpoint_x = 0.0;
double custom_viewpoint_y = 0.0;
double custom_viewpoint_z = 0.0;
int custom_viewpoint = FALSE;
float vertical_spacing = DEFAULT_VERTICAL_SPACING;
float horizontal_spacing = DEFAULT_HORIZONTAL_SPACING;
float node_width = DEFAULT_NODE_WIDTH;
float node_height = DEFAULT_NODE_WIDTH; /* should be the same as the node width */
char *host_name = "all";
int show_all_hosts = TRUE;
int use_textures = TRUE;
int use_text = TRUE;
int use_links = TRUE;
int layout_method = LAYOUT_USER_SUPPLIED;
int coordinates_were_specified = FALSE; /* were drawing coordinates specified with extended host info entries? */
int main(int argc, char **argv) {
int result;
/* reset internal variables */
reset_cgi_vars();
/* Initialize shared configuration variables */
init_shared_cfg_vars(1);
/* read the CGI configuration file */
result = read_cgi_config_file(get_cgi_config_location());
if(result == ERROR) {
document_header();
return ERROR;
}
/* defaults from CGI config file */
layout_method = default_statuswrl_layout_method;
/* get the arguments passed in the URL */
process_cgivars();
document_header();
/* read the main configuration file */
result = read_main_config_file(main_config_file);
if(result == ERROR)
return ERROR;
/* read all object configuration data */
result = read_all_object_configuration_data(main_config_file, READ_ALL_OBJECT_DATA);
if(result == ERROR)
return ERROR;
/* read all status data */
result = read_all_status_data(status_file, READ_ALL_STATUS_DATA);
if(result == ERROR) {
free_memory();
return ERROR;
}
/* get authentication information */
get_authentication_information(&current_authdata);
/* display the 3-D VRML world... */
display_world();
/* free all allocated memory */
free_memory();
return OK;
}
void document_header(void) {
char date_time[MAX_DATETIME_LENGTH];
time_t current_time;
time_t expire_time;
printf("Cache-Control: no-store\r\n");
printf("Pragma: no-cache\r\n");
time(&current_time);
get_time_string(&current_time, date_time, sizeof(date_time), HTTP_DATE_TIME);
printf("Last-Modified: %s\r\n", date_time);
expire_time = 0L;
get_time_string(&expire_time, date_time, sizeof(date_time), HTTP_DATE_TIME);
printf("Expires: %s\r\n", date_time);
printf("Content-Type: x-world/x-vrml\r\n\r\n");
return;
}
int process_cgivars(void) {
char **variables;
int error = FALSE;
int x;
variables = getcgivars();
for(x = 0; variables[x] != NULL; x++) {
/* do some basic length checking on the variable identifier to prevent buffer overflows */
if(strlen(variables[x]) >= MAX_INPUT_BUFFER - 1) {
x++;
continue;
}
/* we found the host argument */
else if(!strcmp(variables[x], "host")) {
x++;
if(variables[x] == NULL) {
error = TRUE;
break;
}
if((host_name = (char *)strdup(variables[x])) == NULL)
host_name = "all";
else
strip_html_brackets(host_name);
if(!strcmp(host_name, "all"))
show_all_hosts = TRUE;
else
show_all_hosts = FALSE;
}
/* we found the no textures argument*/
else if(!strcmp(variables[x], "notextures"))
use_textures = FALSE;
/* we found the no text argument*/
else if(!strcmp(variables[x], "notext"))
use_text = FALSE;
/* we found the no links argument*/
else if(!strcmp(variables[x], "nolinks"))
use_links = FALSE;
/* we found the layout method option */
else if(!strcmp(variables[x], "layout")) {
x++;
if(variables[x] == NULL) {
error = TRUE;
break;
}
layout_method = atoi(variables[x]);
}
/* we found custom viewpoint coord */
else if(!strcmp(variables[x], "viewx")) {
x++;
if(variables[x] == NULL) {
error = TRUE;
break;
}
custom_viewpoint_x = strtod(variables[x], NULL);
custom_viewpoint = TRUE;
}
else if(!strcmp(variables[x], "viewy")) {
x++;
if(variables[x] == NULL) {
error = TRUE;
break;
}
custom_viewpoint_y = strtod(variables[x], NULL);
custom_viewpoint = TRUE;
}
else if(!strcmp(variables[x], "viewz")) {
x++;
if(variables[x] == NULL) {
error = TRUE;
break;
}
custom_viewpoint_z = strtod(variables[x], NULL);
custom_viewpoint = TRUE;
}
}
/* free memory allocated to the CGI variables */
free_cgivars(variables);
return error;
}
/* top-level VRML world generation... */
void display_world(void) {
host *temp_host = NULL;
/* get the url we will use to grab the logo images... */
snprintf(url_logo_images_path, sizeof(url_logo_images_path), "%slogos/", url_images_path);
url_logo_images_path[sizeof(url_logo_images_path) - 1] = '\x0';
/* calculate host drawing coordinates */
calculate_host_coords();
/* calculate world bounds */
calculate_world_bounds();
/* get the floor dimensions */
if(max_x_coord > 0)
floor_width = (float)(max_x_coord - min_x_coord) + (node_width * 2);
else
floor_width = (float)(max_x_coord + min_x_coord) + (node_width * 2);
if(max_z_coord > 0)
floor_depth = (float)(max_z_coord - min_z_coord) + (node_height * 2);
else
floor_depth = (float)(max_z_coord + min_z_coord) + (node_height * 2);
/* write global VRML data */
write_global_vrml_data();
/* no coordinates were specified, so display warning message */
if(coordinates_were_specified == FALSE) {
printf("\n");
printf("Transform{\n");
printf("translation 0.0 0.0 0.0\n");
printf("children[\n");
printf("Billboard{\n");
printf("children[\n");
printf("Shape{\n");
printf("appearance Appearance {\n");
printf("material Material {\n");
printf("diffuseColor 1 0 0\n");
printf("}\n");
printf("}\n");
printf("geometry Text {\n");
printf("string [ \"Error: You have not supplied any 3-D drawing coordinates.\", \"Read the documentation for more information on supplying\", \"3-D drawing coordinates by defining\", \"extended host information entries in your config files.\" ]\n");
printf("fontStyle FontStyle {\n");
printf("family \"TYPEWRITER\"\n");
printf("size 0.3\n");
printf("justify \"MIDDLE\"\n");
printf("}\n");
printf("}\n");
printf("}\n");
printf("]\n");
printf("}\n");
printf("]\n");
printf("}\n");
}
/* coordinates were specified... */
else {
/* draw Nagios icon */
if(layout_method != LAYOUT_USER_SUPPLIED)
draw_process_icon();
/* draw all hosts */
for(temp_host = host_list; temp_host != NULL; temp_host = temp_host->next)
draw_host(temp_host);
/* draw host links */
draw_host_links();
}
return;
}
/******************************************************************/
/************************ UTILITY FUNCTIONS ***********************/
/******************************************************************/
/* calculates how many "layers" separate parent and child - used by collapsed tree layout method */
int host_child_depth_separation(host *parent, host *child) {
int this_depth = 0;
int min_depth = 0;
int have_min_depth = FALSE;
host *temp_host;
if(child == NULL)
return -1;
if(parent == child)
return 0;
if(is_host_immediate_child_of_host(parent, child) == TRUE)
return 1;
for(temp_host = host_list; temp_host != NULL; temp_host = temp_host->next) {
if(is_host_immediate_child_of_host(parent, temp_host) == TRUE) {
this_depth = host_child_depth_separation(temp_host, child);
if(this_depth >= 0 && (have_min_depth == FALSE || (have_min_depth == TRUE && (this_depth < min_depth)))) {
have_min_depth = TRUE;
min_depth = this_depth;
}
}
}
if(have_min_depth == FALSE)
return -1;
else
return min_depth + 1;
}
/* calculates how many hosts reside on a specific "layer" - used by collapsed tree layout method */
int number_of_host_layer_members(host *parent, int layer) {
int current_layer;
int layer_members = 0;
host *temp_host;
for(temp_host = host_list; temp_host != NULL; temp_host = temp_host->next) {
current_layer = host_child_depth_separation(parent, temp_host);
if(current_layer == layer)
layer_members++;
}
return layer_members;
}
/* calculate max number of members on all "layers" beneath and including parent host - used by collapsed tree layout method */
int max_child_host_layer_members(host *parent) {
int current_layer;
int max_members = 1;
int current_members = 0;
for(current_layer = 1;; current_layer++) {
current_members = number_of_host_layer_members(parent, current_layer);
if(current_members <= 0)
break;
if(current_members > max_members)
max_members = current_members;
}
return max_members;
}
/* calculate max drawing width for host and children - used by balanced tree layout method */
int max_child_host_drawing_width(host *parent) {
host *temp_host;
int child_width = 0;
for(temp_host = host_list; temp_host != NULL; temp_host = temp_host->next) {
if(is_host_immediate_child_of_host(parent, temp_host) == TRUE)
child_width += max_child_host_drawing_width(temp_host);
}
/* no children, so set width to 1 for this host */
if(child_width == 0)
return 1;
else
return child_width;
}
/******************************************************************/
/********************* CALCULATION FUNCTIONS **********************/
/******************************************************************/
/* calculates host drawing coordinates */
void calculate_host_coords(void) {
host *this_host;
host *temp_host;
int parent_hosts = 0;
int max_layer_width = 1;
int current_parent_host = 0;
int center_x = 0;
int offset_x = DEFAULT_NODE_WIDTH / 2;
int offset_y = DEFAULT_NODE_WIDTH / 2;
int current_layer = 0;
int layer_members = 0;
int current_layer_member = 0;
int max_drawing_width = 0;
/******************************/
/***** MANUAL LAYOUT MODE *****/
/******************************/
/* user-supplied coords */
if(layout_method == LAYOUT_USER_SUPPLIED) {
/* see which hosts we should draw (only those with 3-D coords) */
for(temp_host = host_list; temp_host != NULL; temp_host = temp_host->next) {
if(temp_host->have_3d_coords == TRUE)
temp_host->should_be_drawn = TRUE;
else
temp_host->should_be_drawn = FALSE;
}
return;
}
/*****************************/
/***** AUTO-LAYOUT MODES *****/
/*****************************/
/* add empty extended host info entries for all hosts that don't have any */
for(temp_host = host_list; temp_host != NULL; temp_host = temp_host->next) {
/* none was found, so add a blank one */
/*
if(temp_hostextinfo==NULL)
add_hostextinfo(temp_host->name,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0,0,0.0,0.0,0.0,0,0);
*/
/* default z coord should 0 for auto-layout modes unless overridden later */
/*
else
*/
temp_host->z_3d = 0.0;
}
/***** COLLAPSED TREE MODE *****/
if(layout_method == LAYOUT_COLLAPSED_TREE) {
/* always use NULL as the "main" host, screen coords/dimensions are adjusted automatically */
this_host = NULL;
/* find total number of immediate parents for this host */
parent_hosts = number_of_immediate_parent_hosts(this_host);
/* find the max layer width we have... */
max_layer_width = max_child_host_layer_members(this_host);
if(parent_hosts > max_layer_width)
max_layer_width = parent_hosts;
/* calculate center x coord */
center_x = (((DEFAULT_NODE_WIDTH * max_layer_width) + (DEFAULT_NODE_HSPACING * (max_layer_width - 1))) / 2) + offset_x;
/* coords for Nagios icon if necessary */
if(this_host == NULL || this_host->parent_hosts == NULL) {
nagios_icon_x = center_x;
nagios_icon_y = offset_y;
draw_nagios_icon = TRUE;
}
/* do we need to draw a link to parent(s)? */
if(this_host != NULL && is_host_immediate_child_of_host(NULL, this_host) == FALSE)
offset_y += DEFAULT_NODE_HEIGHT + DEFAULT_NODE_VSPACING;
/* see which hosts we should draw and calculate drawing coords */
for(temp_host = host_list; temp_host != NULL; temp_host = temp_host->next) {
/* this is an immediate parent of the "main" host we're drawing */
if(is_host_immediate_parent_of_host(this_host, temp_host) == TRUE) {
temp_host->should_be_drawn = TRUE;
temp_host->have_3d_coords = TRUE;
temp_host->x_3d = center_x - (((parent_hosts * DEFAULT_NODE_WIDTH) + ((parent_hosts - 1) * DEFAULT_NODE_HSPACING)) / 2) + (current_parent_host * (DEFAULT_NODE_WIDTH + DEFAULT_NODE_HSPACING)) + (DEFAULT_NODE_WIDTH / 2);
temp_host->y_3d = offset_y;
current_parent_host++;
}
/* this is the "main" host we're drawing */
else if(this_host == temp_host) {
temp_host->should_be_drawn = TRUE;
temp_host->have_3d_coords = TRUE;
temp_host->x_3d = center_x;
temp_host->y_3d = DEFAULT_NODE_HEIGHT + DEFAULT_NODE_VSPACING + offset_y;
}
/* else do not draw this host (we might if its a child - see below, but assume no for now) */
else {
temp_host->should_be_drawn = FALSE;
temp_host->have_3d_coords = FALSE;
}
}
/* TODO: REORDER CHILD LAYER MEMBERS SO THAT WE MINIMIZE LINK CROSSOVERS FROM PARENT HOSTS */
/* draw hosts in child "layers" */
for(current_layer = 1;; current_layer++) {
/* how many members in this layer? */
layer_members = number_of_host_layer_members(this_host, current_layer);
if(layer_members == 0)
break;
current_layer_member = 0;
/* see which hosts are members of this layer and calculate drawing coords */
for(temp_host = host_list; temp_host != NULL; temp_host = temp_host->next) {
/* is this host a member of the current child layer? */
if(host_child_depth_separation(this_host, temp_host) == current_layer) {
temp_host->should_be_drawn = TRUE;
temp_host->have_3d_coords = TRUE;
temp_host->x_3d = center_x - (((layer_members * DEFAULT_NODE_WIDTH) + ((layer_members - 1) * DEFAULT_NODE_HSPACING)) / 2) + (current_layer_member * (DEFAULT_NODE_WIDTH + DEFAULT_NODE_HSPACING)) + (DEFAULT_NODE_WIDTH / 2);
if(this_host == NULL)
temp_host->y_3d = ((DEFAULT_NODE_HEIGHT + DEFAULT_NODE_VSPACING) * current_layer) + offset_y;
else
temp_host->y_3d = ((DEFAULT_NODE_HEIGHT + DEFAULT_NODE_VSPACING) * (current_layer + 1)) + offset_y;
current_layer_member++;
}
}
}
}
/***** "BALANCED" TREE MODE *****/
else if(layout_method == LAYOUT_BALANCED_TREE) {
/* always use NULL as the "main" host, screen coords/dimensions are adjusted automatically */
this_host = NULL;
/* find total number of immediate parents for this host */
parent_hosts = number_of_immediate_parent_hosts(this_host);
/* find the max drawing width we have... */
max_drawing_width = max_child_host_drawing_width(this_host);
if(parent_hosts > max_drawing_width)
max_drawing_width = parent_hosts;
/* calculate center x coord */
center_x = (((DEFAULT_NODE_WIDTH * max_drawing_width) + (DEFAULT_NODE_HSPACING * (max_drawing_width - 1))) / 2) + offset_x;
/* coords for Nagios icon if necessary */
if(this_host == NULL || this_host->parent_hosts == NULL) {
nagios_icon_x = center_x;
nagios_icon_y = offset_y;
draw_nagios_icon = TRUE;
}
/* do we need to draw a link to parent(s)? */
if(this_host != NULL && is_host_immediate_child_of_host(NULL, this_host) == FALSE)
offset_y += DEFAULT_NODE_HEIGHT + DEFAULT_NODE_VSPACING;
/* see which hosts we should draw and calculate drawing coords */
for(temp_host = host_list; temp_host != NULL; temp_host = temp_host->next) {
/* this is an immediate parent of the "main" host we're drawing */
if(is_host_immediate_parent_of_host(this_host, temp_host) == TRUE) {
temp_host->should_be_drawn = TRUE;
temp_host->have_3d_coords = TRUE;
temp_host->x_3d = center_x - (((parent_hosts * DEFAULT_NODE_WIDTH) + ((parent_hosts - 1) * DEFAULT_NODE_HSPACING)) / 2) + (current_parent_host * (DEFAULT_NODE_WIDTH + DEFAULT_NODE_HSPACING)) + (DEFAULT_NODE_WIDTH / 2);
temp_host->y_3d = offset_y;
current_parent_host++;
}
/* this is the "main" host we're drawing */
else if(this_host == temp_host) {
temp_host->should_be_drawn = TRUE;
temp_host->have_3d_coords = TRUE;
temp_host->x_3d = center_x;
temp_host->y_3d = DEFAULT_NODE_HEIGHT + DEFAULT_NODE_VSPACING + offset_y;
}
/* else do not draw this host (we might if its a child - see below, but assume no for now) */
else {
temp_host->should_be_drawn = FALSE;
temp_host->have_3d_coords = FALSE;
}
}
/* draw all children hosts */
calculate_balanced_tree_coords(this_host, center_x, DEFAULT_NODE_HEIGHT + DEFAULT_NODE_VSPACING + offset_y);
}
/***** CIRCULAR LAYOUT MODE *****/
else if(layout_method == LAYOUT_CIRCULAR) {
/* draw process icon */
nagios_icon_x = 0;
nagios_icon_y = 0;
draw_nagios_icon = TRUE;
/* calculate coordinates for all hosts */
calculate_circular_coords();
}
return;
}
/* calculate world dimensions */
void calculate_world_bounds(void) {
host *temp_host;
min_x_coord = 0.0;
min_y_coord = 0.0;
min_z_coord = 0.0;
max_x_coord = 0.0;
max_y_coord = 0.0;
max_z_coord = 0.0;
/* check all extended host entries */
for(temp_host = host_list; temp_host != NULL; temp_host = temp_host->next) {
if(temp_host->have_3d_coords == FALSE) {
temp_host->should_be_drawn = FALSE;
continue;
}
if(temp_host->should_be_drawn == FALSE)
continue;
if(temp_host->x_3d < min_x_coord)
min_x_coord = temp_host->x_3d;
else if(temp_host->x_3d > max_x_coord)
max_x_coord = temp_host->x_3d;
if(temp_host->y_3d < min_y_coord)
min_y_coord = temp_host->y_3d;
else if(temp_host->y_3d > max_y_coord)
max_y_coord = temp_host->y_3d;
if(temp_host->z_3d < min_z_coord)
min_z_coord = temp_host->z_3d;
else if(temp_host->z_3d > max_z_coord)
max_z_coord = temp_host->z_3d;
coordinates_were_specified = TRUE;
}
/* no drawing coordinates were specified */
if(coordinates_were_specified == FALSE) {
min_x_coord = 0.0;
max_x_coord = 0.0;
min_y_coord = 0.0;
max_y_coord = 0.0;
min_z_coord = 0.0;
max_z_coord = 6.0;
}
max_world_size = max_x_coord - min_x_coord;
if(max_world_size < (max_y_coord - min_y_coord))
max_world_size = max_y_coord - min_y_coord;
if(max_world_size < (max_z_coord - min_z_coord))
max_world_size = max_z_coord - min_z_coord;
return;
}
/******************************************************************/
/*********************** DRAWING FUNCTIONS ************************/
/******************************************************************/
/* write global VRML data */
void write_global_vrml_data(void) {
host *temp_host;
float visibility_range = 0.0;
float viewpoint_z = 0.0;
/* write VRML code header */
printf("#VRML V2.0 utf8\n");
/* write world information */
printf("\n");
printf("WorldInfo{\n");
printf("title \"Nagios 3-D Network Status View\"\n");
printf("info [\"Copyright (c) 1999-2002 Ethan Galstad\"\n");
printf("\"egalstad@nagios.org\"]\n");
printf("}\n");
/* background color */
printf("\n");
printf("Background{\n");
printf("skyColor 0.1 0.1 0.15\n");
printf("}\n");
/* calculate visibility range - don't let it get too low */
visibility_range = (max_world_size * 2.0);
if(visibility_range < 25.0)
visibility_range = 25.0;
/* write fog information */
printf("\n");
printf("Fog{\n");
printf("color 0.1 0.1 0.15\n");
printf("fogType \"EXPONENTIAL\"\n");
printf("visibilityRange %2.2f\n", visibility_range);
printf("}\n");
/* custom viewpoint */
if(custom_viewpoint == TRUE) {
printf("\n");
printf("Viewpoint{\n");
printf("position %2.2f %2.2f %2.2f\n", custom_viewpoint_x, custom_viewpoint_y, custom_viewpoint_z);
printf("fieldOfView 0.78\n");
printf("description \"Entry Viewpoint\"\n");
printf("}\n");
}
/* host close-up viewpoint */
if(show_all_hosts == FALSE) {
temp_host = find_host(host_name);
if(temp_host != NULL && temp_host->have_3d_coords == TRUE) {
printf("\n");
printf("Viewpoint{\n");
printf("position %2.3f %2.3f %2.3f\n", temp_host->x_3d, temp_host->y_3d, temp_host->z_3d + 5.0);
printf("fieldOfView 0.78\n");
printf("description \"Host Close-Up Viewpoint\"\n");
printf("}\n");
}
}
/* calculate z coord for default viewpoint - don't get too close */
viewpoint_z = max_world_size;
if(viewpoint_z < 10.0)
viewpoint_z = 10.0;
/* default viewpoint */
printf("\n");
printf("Viewpoint{\n");
printf("position %2.2f %2.2f %2.2f\n", min_x_coord + ((max_x_coord - min_x_coord) / 2.0), min_y_coord + ((max_y_coord - min_y_coord) / 2.0), viewpoint_z);
printf("fieldOfView 0.78\n");
printf("description \"Default Viewpoint\"\n");
printf("}\n");
/* problem timer */
printf("DEF ProblemTimer TimeSensor{\n");
printf("loop TRUE\n");
printf("cycleInterval 5\n");
printf("}\n");
/* host text prototype */
printf("PROTO HostText[\n");
printf("field MFString the_text [\"\"]\n");
printf("field SFColor font_color 0.6 0.6 0.6");
printf("]\n");
printf("{\n");
printf("Billboard{\n");
printf("children[\n");
printf("Shape{\n");
printf("appearance Appearance {\n");
printf("material Material {\n");
printf("diffuseColor IS font_color\n");
printf("}\n");
printf("}\n");
printf("geometry Text {\n");
printf("string IS the_text\n");
printf("fontStyle FontStyle {\n");
printf("family \"TYPEWRITER\"\n");
printf("size 0.1\n");
printf("justify \"MIDDLE\"\n");
printf("}\n");
printf("}\n");
printf("}\n");
printf("]\n");
printf("}\n");
printf("}\n");
/* include user-defined world */
if(statuswrl_include != NULL && coordinates_were_specified == TRUE && layout_method == LAYOUT_USER_SUPPLIED) {
printf("\n");
printf("Inline{\n");
printf("url \"%s%s\"\n", url_html_path, statuswrl_include);
printf("}\n");
}
return;
}
/* draws a host */
void draw_host(host *temp_host) {
hoststatus *temp_hoststatus = NULL;
char state_string[16] = "";
double x, y, z;
char *vrml_safe_hostname = NULL;
int a, ch;
if(temp_host == NULL)
return;
/* make sure we have the coordinates */
if(temp_host->have_3d_coords == FALSE)
return;
else {
x = temp_host->x_3d;
y = temp_host->y_3d;
z = temp_host->z_3d;
}
/* make the host name safe for embedding in VRML */
vrml_safe_hostname = (char *)strdup(temp_host->name);
if(vrml_safe_hostname == NULL)
return;
for(a = 0; vrml_safe_hostname[a] != '\x0'; a++) {
ch = vrml_safe_hostname[a];
if((ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z') && (ch < '0' || ch > '9'))
vrml_safe_hostname[a] = '_';
}
/* see if user is authorized to view this host */
if(is_authorized_for_host(temp_host, &current_authdata) == FALSE)
return;
/* get the status of the host */
temp_hoststatus = find_hoststatus(temp_host->name);
printf("\n");
/* host object */
printf("Anchor{\n");
printf("children[\n");
printf("Transform {\n");
printf("translation %2.2f %2.2f %2.2f\n", x, y, z);
printf("children [\n");
printf("DEF Host%s Shape{\n", vrml_safe_hostname);
printf("appearance Appearance{\n");
printf("material DEF HostMat%s Material{\n", vrml_safe_hostname);
if(temp_hoststatus == NULL)
printf("emissiveColor 0.2 0.2 0.2\ndiffuseColor 0.2 0.2 0.2\n");
else if(temp_hoststatus->status == SD_HOST_UP)
printf("emissiveColor 0.2 1.0 0.2\ndiffuseColor 0.2 1.0 0.2\n");
else
printf("emissiveColor 1.0 0.2 0.2\ndiffuseColor 1.0 0.2 0.2\n");
printf("transparency 0.4\n");
printf("}\n");
if(use_textures == TRUE && temp_host->vrml_image != NULL) {
printf("texture ImageTexture{\n");
printf("url \"%s%s\"\n", url_logo_images_path, temp_host->vrml_image);
printf("}\n");
}
printf("}\n");
printf("geometry Box{\n");
printf("size %2.2f %2.2f %2.2f\n", node_width, node_width, node_width);
printf("}\n");
printf("}\n");
printf("]\n");
printf("}\n");
printf("]\n");
printf("description \"View status details for host '%s' (%s)\"\n", temp_host->name, temp_host->alias);
printf("url \"%s?host=%s\"\n", STATUS_CGI, temp_host->name);
printf("}\n");
/* draw status text */
if(use_text == TRUE) {
printf("\n");
printf("Transform{\n");
printf("translation %2.3f %2.3f %2.3f\n", x, y + DEFAULT_NODE_WIDTH, z);
printf("children[\n");
printf("HostText{\n");
if(temp_hoststatus != NULL) {
if(temp_hoststatus->status == SD_HOST_UP)
printf("font_color 0 1 0\n");
else if(temp_hoststatus->status == SD_HOST_DOWN || temp_hoststatus->status == SD_HOST_UNREACHABLE)
printf("font_color 1 0 0\n");
}
printf("the_text [\"%s\", \"%s\", ", temp_host->name, temp_host->alias);
if(temp_hoststatus == NULL)
strcpy(state_string, "UNKNOWN");
else {
if(temp_hoststatus->status == SD_HOST_DOWN)
strcpy(state_string, "DOWN");
else if(temp_hoststatus->status == SD_HOST_UNREACHABLE)
strcpy(state_string, "UNREACHABLE");
else if(temp_hoststatus->status == HOST_PENDING)
strcpy(state_string, "PENDING");
else
strcpy(state_string, "UP");
}
printf("\"%s\"]\n", state_string);
printf("}\n");
printf("]\n");
printf("}\n");
}
/* host is down or unreachable, so make it fade in and out */
if(temp_hoststatus != NULL && (temp_hoststatus->status == SD_HOST_DOWN || temp_hoststatus->status == SD_HOST_UNREACHABLE))
printf("ROUTE ProblemTimer.fraction_changed TO HostMat%s.set_transparency\n", vrml_safe_hostname);
free(vrml_safe_hostname);
return;
}
/* draw links between hosts */
void draw_host_links(void) {
host *parent_host;
host *child_host;
if(use_links == FALSE)
return;
for(child_host = host_list; child_host != NULL; child_host = child_host->next) {
if(child_host->have_3d_coords == FALSE)
continue;
/* check authorization */
if(is_authorized_for_host(child_host, &current_authdata) == FALSE)
continue;
/* draw a link from this host to all of its parent hosts */
for(parent_host = host_list; parent_host != NULL; parent_host = parent_host->next) {
if(is_host_immediate_child_of_host(child_host, parent_host) == TRUE) {
if(parent_host->have_3d_coords == FALSE)
continue;
/* check authorization */
if(is_authorized_for_host(parent_host, &current_authdata) == FALSE)
continue;
/* draw the link between the child and parent hosts */
draw_host_link(parent_host, parent_host->x_3d, parent_host->y_3d, parent_host->z_3d, child_host->x_3d, child_host->y_3d, child_host->z_3d);
}
}
}
return;
}
/* draws a link from a parent host to a child host */
void draw_host_link(host *hst, double x0, double y0, double z0, double x1, double y1, double z1) {
printf("\n");
if(hst != NULL)
printf("# Host '%s' LINK\n", hst->name);
printf("Shape{\n");
printf("appearance DEF MATslategrey_0_ Appearance {\n");
printf("material Material {\n");
printf("diffuseColor 0.6 0.6 0.6\n");
printf("ambientIntensity 0.5\n");
printf("emissiveColor 0.6 0.6 0.6\n");
printf("}\n");
printf("}\n");
printf("geometry IndexedLineSet{\n");
printf("coord Coordinate{\n");
printf("point [ %2.3f %2.3f %2.3f, %2.3f %2.3f %2.3f ]\n", x0, y0, z0, x1, y1, z1);
printf("}\n");
printf("coordIndex [ 0,1,-1 ]\n");
printf("}\n");
printf("}\n");
return;
}
/* draw process icon */
void draw_process_icon(void) {
host *child_host;
if(draw_nagios_icon == FALSE)
return;
/* draw process icon */
printf("\n");
printf("Anchor{\n");
printf("children[\n");
printf("Transform {\n");
printf("translation %2.2f %2.2f %2.2f\n", nagios_icon_x, nagios_icon_y, 0.0);
printf("children [\n");
printf("DEF ProcessNode Shape{\n");
printf("appearance Appearance{\n");
printf("material Material{\n");
printf("emissiveColor 0.5 0.5 0.5\n");
printf("diffuseColor 0.5 0.5 0.5\n");
printf("transparency 0.2\n");
printf("}\n");
if(use_textures == TRUE) {
printf("texture ImageTexture{\n");
printf("url \"%s%s\"\n", url_logo_images_path, NAGIOS_VRML_IMAGE);
printf("}\n");
}
printf("}\n");
printf("geometry Box{\n");
printf("size %2.2f %2.2f %2.2f\n", node_width * 3.0, node_width * 3.0, node_width * 3.0);
printf("}\n");
printf("}\n");
printf("]\n");
printf("}\n");
printf("]\n");
printf("description \"View Nagios Process Information\"\n");
printf("url \"%s?type=%d\"\n", EXTINFO_CGI, DISPLAY_PROCESS_INFO);
printf("}\n");
if(use_links == FALSE)
return;
/* draw links to immediate child hosts */
for(child_host = host_list; child_host != NULL; child_host = child_host->next) {
if(child_host->have_3d_coords == FALSE)
continue;
/* check authorization */
if(is_authorized_for_host(child_host, &current_authdata) == FALSE)
continue;
/* draw a link to the host */
if(is_host_immediate_child_of_host(NULL, child_host) == TRUE)
draw_host_link(NULL, nagios_icon_x, nagios_icon_y, 0.0, child_host->x_3d, child_host->y_3d, child_host->z_3d);
}
return;
}
/******************************************************************/
/***************** COORDINATE CALCULATION FUNCTIONS ***************/
/******************************************************************/
/* calculates coords of a host's children - used by balanced tree layout method */
void calculate_balanced_tree_coords(host *parent, int x, int y) {
int parent_drawing_width;
int start_drawing_x;
int current_drawing_x;
int this_drawing_width;
host *temp_host;
/* calculate total drawing width of parent host */
parent_drawing_width = max_child_host_drawing_width(parent);
/* calculate starting x coord */
start_drawing_x = x - (((DEFAULT_NODE_WIDTH * parent_drawing_width) + (DEFAULT_NODE_HSPACING * (parent_drawing_width - 1))) / 2);
current_drawing_x = start_drawing_x;
/* calculate coords for children */
for(temp_host = host_list; temp_host != NULL; temp_host = temp_host->next) {
if(is_host_immediate_child_of_host(parent, temp_host) == TRUE) {
/* get drawing width of child host */
this_drawing_width = max_child_host_drawing_width(temp_host);
temp_host->x_3d = current_drawing_x + (((DEFAULT_NODE_WIDTH * this_drawing_width) + (DEFAULT_NODE_HSPACING * (this_drawing_width - 1))) / 2);
temp_host->y_3d = y + DEFAULT_NODE_HEIGHT + DEFAULT_NODE_VSPACING;
temp_host->have_3d_coords = TRUE;
temp_host->should_be_drawn = TRUE;
current_drawing_x += (this_drawing_width * DEFAULT_NODE_WIDTH) + ((this_drawing_width - 1) * DEFAULT_NODE_HSPACING) + DEFAULT_NODE_HSPACING;
/* recurse into child host ... */
calculate_balanced_tree_coords(temp_host, temp_host->x_3d, temp_host->y_3d);
}
}
return;
}
/* calculate coords of all hosts in circular layout method */
void calculate_circular_coords(void) {
/* calculate all host coords, starting with first layer */
calculate_circular_layer_coords(NULL, 0.0, 360.0, 1, CIRCULAR_DRAWING_RADIUS);
return;
}
/* calculates coords of all hosts in a particular "layer" in circular layout method */
void calculate_circular_layer_coords(host *parent, double start_angle, double useable_angle, int layer, int radius) {
int parent_drawing_width = 0;
int this_drawing_width = 0;
int immediate_children = 0;
double current_drawing_angle = 0.0;
double this_drawing_angle = 0.0;
double available_angle = 0.0;
double clipped_available_angle = 0.0;
double x_coord = 0.0;
double y_coord = 0.0;
host *temp_host;
/* get the total number of immediate children to this host */
immediate_children = number_of_immediate_child_hosts(parent);
/* bail out if we're done */
if(immediate_children == 0)
return;
/* calculate total drawing "width" of parent host */
parent_drawing_width = max_child_host_drawing_width(parent);
/* calculate initial drawing angle */
current_drawing_angle = start_angle;
/* calculate coords for children */
for(temp_host = host_list; temp_host != NULL; temp_host = temp_host->next) {
if(is_host_immediate_child_of_host(parent, temp_host) == TRUE) {
/* get drawing width of child host */
this_drawing_width = max_child_host_drawing_width(temp_host);
/* calculate angle this host gets for drawing */
available_angle = useable_angle * ((double)this_drawing_width / (double)parent_drawing_width);
/* clip available angle if necessary */
/* this isn't really necessary, but helps keep things looking a bit more sane with less potential connection crossover */
clipped_available_angle = 360.0 / layer;
if(available_angle < clipped_available_angle)
clipped_available_angle = available_angle;
/* calculate the exact angle at which we should draw this child */
this_drawing_angle = current_drawing_angle + (available_angle / 2.0);
/* compensate for angle overflow */
while(this_drawing_angle >= 360.0)
this_drawing_angle -= 360.0;
while(this_drawing_angle < 0.0)
this_drawing_angle += 360.0;
/* calculate drawing coords of this host using good ol' geometry... */
x_coord = -(sin(-this_drawing_angle * (M_PI / 180.0)) * radius);
y_coord = -(sin((90 + this_drawing_angle) * (M_PI / 180.0)) * radius);
temp_host->x_3d = (int)x_coord;
temp_host->y_3d = (int)y_coord;
temp_host->have_3d_coords = TRUE;
temp_host->should_be_drawn = TRUE;
/* recurse into child host ... */
calculate_circular_layer_coords(temp_host, current_drawing_angle + ((available_angle - clipped_available_angle) / 2), clipped_available_angle, layer + 1, radius + CIRCULAR_DRAWING_RADIUS);
/* increment current drawing angle */
current_drawing_angle += available_angle;
}
}
return;
}