From 9069c9cd40da6cb44874ed282adb5e00d4f2ec40 Mon Sep 17 00:00:00 2001 From: massie Date: Fri, 14 Jan 2005 23:36:46 +0000 Subject: [PATCH] *** empty log message *** --- monitor-core/configure.in | 2 +- monitor-core/gmond/conf.c | 2 + monitor-core/gmond/conf.h | 59 +-- monitor-core/gmond/gmond.c | 942 ++++++++++++++++++++++------------ monitor-core/gmond/gmond.conf | 34 +- monitor-core/lib/apr_net.c | 8 +- 6 files changed, 649 insertions(+), 398 deletions(-) diff --git a/monitor-core/configure.in b/monitor-core/configure.in index d12efc806..14b532f72 100644 --- a/monitor-core/configure.in +++ b/monitor-core/configure.in @@ -392,7 +392,7 @@ echo " / /_/ / /_/ / / / / /_/ / / / /_/ /" echo " \____/\__,_/_/ /_/\__, /_/_/\__,_/" echo " /____/" echo "" -echo "Copyright (c) 2004 University of California, Berkeley" +echo "Copyright (c) 2005 University of California, Berkeley" echo "" echo "Version: $GANGLIA_VERSION ($GANGLIA_RELEASE_NAME)" echo "Library: Release $LT_RELEASE $LT_CURRENT:$LT_REVISION:$LT_AGE" diff --git a/monitor-core/gmond/conf.c b/monitor-core/gmond/conf.c index 1911d9f5b..e5250e066 100644 --- a/monitor-core/gmond/conf.c +++ b/monitor-core/gmond/conf.c @@ -1,6 +1,7 @@ #include #include "conf.h" +#if 0 extern cfg_t *config_file; /* This function is necessary only because I need @@ -34,3 +35,4 @@ value_callback(cfg_t *cfg, cfg_opt_t *opt, const char *value, void *result) fprintf(stderr,"CALLED\n"); return 0; } +#endif diff --git a/monitor-core/gmond/conf.h b/monitor-core/gmond/conf.h index 7e0d679cf..fec9fa966 100644 --- a/monitor-core/gmond/conf.h +++ b/monitor-core/gmond/conf.h @@ -8,15 +8,13 @@ in order for the documentation to be in order with the code #include "confuse.h" -void init_validate_funcs(void); - #define DEFAULT_CONFIGURATION "\ behavior { \n\ setuid = no \n\ user = nobody \n\ } \n\ udp_send_channel { \n\ - ip = 127.0.0.1 \n\ + mcast_join = 239.2.11.71 \n\ port = 8649 \n\ } \n\ udp_recv_channel { \n\ @@ -28,26 +26,18 @@ tcp_accept_channel { \n\ port = 8666 \n\ } \n\ collection_group { \n\ - name = \"cpu_stat\" \n\ + collect_every = 60 \n\ metric { \n\ name = \"cpu_user\" \n\ - absolute_minimum = 0 \n\ - absolute_maximum = 100 \n\ } \n\ metric { \n\ - name = \"cpu_sys\" \n\ - absolute_minimum = 0 \n\ - absolute_maximum = 100 \n\ + name = \"cpu_system\" \n\ } \n\ metric { \n\ name = \"cpu_idle\" \n\ - absolute_minimum = 0 \n\ - absolute_maximum = 100 \n\ } \n\ metric { \n\ name = \"cpu_nice\" \n\ - absolute_minimum = 0 \n\ - absolute_maximum = 100 \n\ } \n\ } \n\ " @@ -77,7 +67,6 @@ static cfg_opt_t udp_send_channel_opts[] = { CFG_STR("mcast_if", NULL, CFGF_NONE), CFG_STR("ip", NULL, CFGF_NONE ), CFG_INT("port", -1, CFGF_NONE ), - CFG_STR("protocol", "xdr", CFGF_NONE), CFG_END() }; @@ -86,7 +75,6 @@ static cfg_opt_t udp_recv_channel_opts[] = { CFG_STR("bind", NULL, CFGF_NONE ), CFG_INT("port", -1, CFGF_NONE ), CFG_STR("mcast_if", NULL, CFGF_NONE), - CFG_STR("protocol", "xdr", CFGF_NONE), CFG_STR("allow_ip", NULL, CFGF_NONE), CFG_STR("allow_mask", NULL, CFGF_NONE), CFG_END() @@ -96,64 +84,33 @@ static cfg_opt_t tcp_accept_channel_opts[] = { CFG_STR("bind", NULL, CFGF_NONE ), CFG_INT("port", -1, CFGF_NONE ), CFG_STR("interface", NULL, CFGF_NONE), - CFG_STR("protocol", "xml", CFGF_NONE), CFG_STR("allow_ip", NULL, CFGF_NONE), CFG_STR("allow_mask", NULL, CFGF_NONE), CFG_END() }; -int metric_validate_func(cfg_t *cfg, cfg_opt_t *opt); -cfg_callback_t value_cb; - static cfg_opt_t metric_opts[] = { - CFG_BOOL( "absolute_minimum_given", 0, CFGF_NONE ), - CFG_FLOAT("absolute_minimum", 0, CFGF_NONE ), - CFG_BOOL( "absolute_minimum_alert_given", 0, CFGF_NONE ), - CFG_FLOAT("absolute_minimum_alert", 0, CFGF_NONE ), - CFG_BOOL( "absolute_minimum_warning_given", 0, CFGF_NONE ), - CFG_FLOAT("absolute_minimum_warning", 0, CFGF_NONE ), - CFG_BOOL( "absolute_maximum_warning_given", 0, CFGF_NONE ), - CFG_FLOAT("absolute_maximum_warning", 0, CFGF_NONE ), - CFG_BOOL( "absolute_maximum_alert_given", 0, CFGF_NONE ), - CFG_FLOAT("absolute_maximum_alert", 0, CFGF_NONE ), - CFG_BOOL( "absolute_maximum_given", 0, CFGF_NONE ), - CFG_FLOAT("absolute_maximum", 0, CFGF_NONE ), - CFG_BOOL( "relative_change_normal_given", 0, CFGF_NONE), - CFG_FLOAT("relative_change_normal", 0, CFGF_NONE), - CFG_BOOL( "relative_change_warning_given", 0, CFGF_NONE), - CFG_FLOAT("relative_change_warning", 0, CFGF_NONE), - CFG_BOOL( "relative_change_alert_given", 0, CFGF_NONE), - CFG_FLOAT("relative_change_alert", 0, CFGF_NONE), - CFG_STR("units", NULL, CFGF_NONE ), CFG_STR("name", NULL, CFGF_NONE ), - CFG_INT("current_state", 0, CFGF_NONE), /* high_alert, high_warning, normal, low_warning, low_alert */ - CFG_END() -}; - -static cfg_opt_t static_collection_group_opts[] = { - CFG_STR("name", NULL, CFGF_NONE), - CFG_INT("collection_interval", 60, CFGF_NONE), + CFG_FLOAT("value_threshold", -1, CFGF_NONE), CFG_END() }; -/* Group with metrics that change... */ static cfg_opt_t collection_group_opts[] = { CFG_STR("name", NULL, CFGF_NONE), CFG_SEC("metric", metric_opts, CFGF_MULTI), - CFG_INT("collection_interval", 60, CFGF_NONE), - CFG_INT("announce_interval", 3600, CFGF_NONE), /* tmax */ - CFG_INT("lifetime", 0, CFGF_NONE), /* dmax */ + CFG_BOOL("collect_once", 0, CFGF_NONE), + CFG_INT("collect_every", 60, CFGF_NONE), + CFG_INT("time_threshold", 3600, CFGF_NONE), /* tmax */ + CFG_INT("lifetime", 0, CFGF_NONE), /* dmax */ CFG_END() }; - static cfg_opt_t gmond_opts[] = { CFG_SEC("cluster", cluster_opts, CFGF_NONE), CFG_SEC("behavior", behavior_opts, CFGF_NONE), CFG_SEC("udp_send_channel", udp_send_channel_opts, CFGF_MULTI), CFG_SEC("udp_recv_channel", udp_recv_channel_opts, CFGF_MULTI), CFG_SEC("tcp_accept_channel", tcp_accept_channel_opts, CFGF_MULTI), - CFG_SEC("static_collection_group", static_collection_group_opts, CFGF_MULTI), CFG_SEC("collection_group", collection_group_opts, CFGF_MULTI), CFG_END() }; diff --git a/monitor-core/gmond/gmond.c b/monitor-core/gmond/gmond.c index b1c67e7fb..5e97dfeb7 100644 --- a/monitor-core/gmond/gmond.c +++ b/monitor-core/gmond/gmond.c @@ -41,18 +41,40 @@ int deaf; int mute; /* Maximum UDP message size.. TODO: allow tweakability */ int max_udp_message_len = 1472; /* mtu 1500 - 28 bytes for IP/UDP headers */ + +#if 0 /* The pollset for incoming UDP messages */ apr_pollset_t *udp_recv_pollset = NULL; /* The access control list for each of the UDP channels */ apr_array_header_t *udp_recv_acl_array = NULL; +#endif /* The array for outgoing UDP message channels */ apr_array_header_t *udp_send_array = NULL; +/* The array for outgoing TCP message channels (later) */ +apr_array_header_t *tcp_send_array = NULL; +enum Ganglia_channel_types { + TCP_ACCEPT_CHANNEL, + UDP_RECV_CHANNEL +}; +typedef enum Ganglia_channel_types Ganglia_channel_types; + +struct Ganglia_channel { + Ganglia_channel_types type; + apr_ipsubnet_t *acl; +}; +typedef struct Ganglia_channel Ganglia_channel; + +/* This pollset holds the tcp_accept and udp_recv channels */ +apr_pollset_t *listen_channels = NULL; + +#if 0 /* The pollset for incoming TCP requests */ apr_pollset_t *tcp_accept_pollset = NULL; /* The access control list for each of the TCP accept channels */ apr_array_header_t *tcp_accept_acl_array = NULL; +#endif /* The hash to hold the hosts (key = host IP) */ apr_hash_t *hosts = NULL; @@ -78,6 +100,7 @@ struct Ganglia_host_data { }; typedef struct Ganglia_host_data Ganglia_host_data; +/* This is the structure of the data save to each host->metric hash */ struct Ganglia_metric_data { /* The ganglia message */ Ganglia_message message; @@ -92,11 +115,30 @@ apr_hash_t *metric_callbacks = NULL; /* The "metrics" hash contains values of type "Ganglia_metric_callback" */ /* This is where libmetrics meets gmond */ struct Ganglia_metric_callback { - char *name; /* metric name */ + char *name; /* metric name */ + float value_threshold;/* the value threshold */ + Ganglia_25metric *info;/* the information about this metric */ g_val_t (*cb)(void); /* callback function */ + g_val_t now; /* the current value */ + g_val_t last; /* the last value */ + Ganglia_message msg; /* the message to send */ }; typedef struct Ganglia_metric_callback Ganglia_metric_callback; +/* This is the structure of a collection group */ +struct Ganglia_collection_group { + apr_time_t next_collect; /* When to collect next */ + apr_time_t next_send; /* When to send next (tmax) */ + int once; + int collect_every; + int time_threshold; + apr_array_header_t *metric_array; +}; +typedef struct Ganglia_collection_group Ganglia_collection_group; + +/* This is the array of collection groups that we are processing... */ +apr_array_header_t *collection_groups = NULL; + static void cleanup_configuration_file(void) { @@ -108,8 +150,6 @@ process_configuration_file(void) { config_file = cfg_init( gmond_opts, CFGF_NOCASE ); - init_validate_funcs(); /* in config.c */ - switch( cfg_parse( config_file, args_info.conf_arg ) ) { case CFG_FILE_ERROR: @@ -227,23 +267,23 @@ process_deaf_mute_mode( void ) } static void -setup_udp_recv_pollset( void ) +setup_listen_channels_pollset( void ) { apr_status_t status; - /* We will open sockets to listen for messages */ - int i, num_udp_recv_channels = cfg_size( config_file, "udp_recv_channel"); - - /* Create my UDP recv pollset */ - apr_pollset_create(&udp_recv_pollset, num_udp_recv_channels, global_context, 0); + int i; + int num_udp_recv_channels = cfg_size( config_file, "udp_recv_channel"); + int num_tcp_accept_channels = cfg_size( config_file, "tcp_accept_channel"); + int total_listen_channels = num_udp_recv_channels + num_tcp_accept_channels; + Ganglia_channel *channel; - /* Create my UDP recv access control array */ - udp_recv_acl_array = apr_array_make( global_context, num_udp_recv_channels, - sizeof(apr_ipsubnet_t *)); + /* Create my incoming pollset */ + apr_pollset_create(&listen_channels, total_listen_channels, global_context, 0); + /* Process all the udp_recv_channels */ for(i = 0; i< num_udp_recv_channels; i++) { cfg_t *udp_recv_channel; - char *mcast_join, *mcast_if, *bindaddr, *protocol, *allow_ip, *allow_mask; + char *mcast_join, *mcast_if, *bindaddr, *allow_ip, *allow_mask; int port; apr_socket_t *socket = NULL; apr_ipsubnet_t *ipsub = NULL; @@ -255,15 +295,13 @@ setup_udp_recv_pollset( void ) mcast_if = cfg_getstr( udp_recv_channel, "mcast_if" ); port = cfg_getint( udp_recv_channel, "port"); bindaddr = cfg_getstr( udp_recv_channel, "bind"); - protocol = cfg_getstr( udp_recv_channel, "protocol"); allow_ip = cfg_getstr( udp_recv_channel, "allow_ip"); allow_mask = cfg_getstr( udp_recv_channel, "allow_mask"); - debug_msg("udp_recv_channel mcast_join=%s mcast_if=%s port=%d bind=%s protocol=%s\n", + debug_msg("udp_recv_channel mcast_join=%s mcast_if=%s port=%d bind=%s\n", mcast_join? mcast_join:"NULL", mcast_if? mcast_if:"NULL", port, - bindaddr? bindaddr: "NULL", - protocol? protocol:"NULL"); + bindaddr? bindaddr: "NULL"); /* Create a sub-pool for this channel */ apr_pool_create(&pool, global_context); @@ -296,20 +334,91 @@ setup_udp_recv_pollset( void ) socket_pollfd.desc_type = APR_POLL_SOCKET; socket_pollfd.reqevents = APR_POLLIN; socket_pollfd.desc.s = socket; - socket_pollfd.client_data = protocol; + + channel = apr_pcalloc( pool, sizeof(Ganglia_channel)); + if(!channel) + { + fprintf(stderr,"Unable to malloc memory for channel. Exiting. \n"); + exit(1); + } + + /* Mark this channel as a udp_recv_channel */ + channel->type = UDP_RECV_CHANNEL; + + /* Save the ACL information */ + if(allow_ip) + { + status = apr_ipsubnet_create(&ipsub, allow_ip, allow_mask, pool); + if(status != APR_SUCCESS) + { + fprintf(stderr,"Unable to build ACL for ip=%s mask=%s. Exiting.\n", + allow_ip, allow_mask); + exit(1); + } + } + channel->acl = ipsub; + + /* Save the pointer to this socket specific data */ + socket_pollfd.client_data = channel; /* Add the socket to the pollset */ - status = apr_pollset_add(udp_recv_pollset, &socket_pollfd); + status = apr_pollset_add(listen_channels, &socket_pollfd); if(status != APR_SUCCESS) { fprintf(stderr,"Failed to add socket to pollset. Exiting.\n"); exit(1); } + } + + /* Process all the tcp_accept_channels */ + for(i=0; i< num_tcp_accept_channels; i++) + { + cfg_t *tcp_accept_channel = cfg_getnsec( config_file, "tcp_accept_channel", i); + char *bindaddr, *allow_ip, *allow_mask, *interface; + int port; + apr_socket_t *socket = NULL; + apr_ipsubnet_t *ipsub = NULL; + apr_pollfd_t socket_pollfd; + apr_pool_t *pool = NULL; + + port = cfg_getint( tcp_accept_channel, "port"); + bindaddr = cfg_getstr( tcp_accept_channel, "bind"); + allow_ip = cfg_getstr( tcp_accept_channel, "allow_ip"); + allow_mask = cfg_getstr( tcp_accept_channel, "allow_mask"); + interface = cfg_getstr( tcp_accept_channel, "interface"); + + debug_msg("tcp_accept_channel bind=%s port=%d\n", + bindaddr? bindaddr: "NULL", port); + + /* Create a subpool context */ + apr_pool_create(&pool, global_context); + + /* Create the socket for the channel */ + socket = create_tcp_server(pool, port, bindaddr, interface); + if(!socket) + { + fprintf(stderr,"Unable to create tcp_accept_channel. Exiting.\n"); + exit(1); + } + + /* Build the socket poll file descriptor structure */ + socket_pollfd.desc_type = APR_POLL_SOCKET; + socket_pollfd.reqevents = APR_POLLIN; + socket_pollfd.desc.s = socket; + + channel = apr_pcalloc( pool, sizeof(Ganglia_channel)); + if(!channel) + { + fprintf(stderr,"Unable to malloc data for channel. Exiting.\n"); + exit(1); + } + + channel->type = TCP_ACCEPT_CHANNEL; /* Save the ACL information */ if(allow_ip) { - status = apr_ipsubnet_create(&ipsub, allow_ip, allow_mask, global_context); + status = apr_ipsubnet_create(&ipsub, allow_ip, allow_mask, pool); if(status != APR_SUCCESS) { fprintf(stderr,"Unable to build ACL for ip=%s mask=%s. Exiting.\n", @@ -317,8 +426,16 @@ setup_udp_recv_pollset( void ) exit(1); } } - /* ipsub of NULL means no acl in effect */ - *(apr_ipsubnet_t **)apr_array_push(udp_recv_acl_array) = ipsub; + channel->acl = ipsub; + socket_pollfd.client_data = channel; + + /* Add the socket to the pollset */ + status = apr_pollset_add(listen_channels, &socket_pollfd); + if(status != APR_SUCCESS) + { + fprintf(stderr,"Failed to add socket to pollset. Exiting.\n"); + exit(1); + } } } @@ -466,114 +583,70 @@ Ganglia_message_save( Ganglia_host_data *host, Ganglia_message *message ) } static void -poll_udp_recv_channels(apr_interval_time_t timeout) +process_udp_recv_channel(const apr_pollfd_t *desc) { apr_status_t status; - const apr_pollfd_t *descs = NULL; - apr_int32_t num = 0; - - /* Poll for data with given timeout */ - status = apr_pollset_poll(udp_recv_pollset, timeout, &num, &descs); + apr_socket_t *socket; + apr_sockaddr_t *remotesa = NULL; + char remoteip[256]; + char buf[max_udp_message_len]; + apr_size_t len = max_udp_message_len; + Ganglia_channel *channel; + XDR x; + Ganglia_message msg; + Ganglia_host_data *hostdata = NULL; + + socket = desc->desc.s; + /* We could also use the apr_socket_data_get/set() functions + * to have per socket user data .. see APR docs */ + channel = desc->client_data; + + apr_socket_addr_get(&remotesa, APR_REMOTE, socket); + + /* Grab the data */ + status = apr_socket_recvfrom(remotesa, socket, 0, buf, &len); if(status != APR_SUCCESS) - return; - - if(num>0) { - int i; - - /* We have data to read */ - for(i=0; i< num; i++) - { - apr_socket_t *socket; - apr_sockaddr_t *remotesa = NULL; - char *protocol, remoteip[256]; - apr_ipsubnet_t *ipsub; - char buf[max_udp_message_len]; - apr_size_t len = max_udp_message_len; - - socket = descs[i].desc.s; - /* We could also use the apr_socket_data_get/set() functions - * to have per socket user data .. see APR docs */ - protocol = descs[i].client_data; - - apr_socket_addr_get(&remotesa, APR_REMOTE, socket); - - /* Grab the data */ - status = apr_socket_recvfrom(remotesa, socket, 0, buf, &len); - if(status != APR_SUCCESS) - { - continue; - } + return; + } - /* This function is in ./lib/apr_net.c and not APR. The - * APR counterpart is apr_sockaddr_ip_get() but we don't - * want to malloc memory evertime we call this */ - apr_sockaddr_ip_buffer_get(remoteip, 256, remotesa); + /* This function is in ./lib/apr_net.c and not APR. The + * APR counterpart is apr_sockaddr_ip_get() but we don't + * want to malloc memory evertime we call this */ + apr_sockaddr_ip_buffer_get(remoteip, 256, remotesa); - /* Check the ACL (we can make this better later) */ - ipsub = ((apr_ipsubnet_t **)(udp_recv_acl_array->elts))[i]; - if(ipsub) - { - if(!apr_ipsubnet_test( ipsub, remotesa)) - { - debug_msg("Ignoring data from %s\n", remoteip); - continue; /* to the next channel that needs read */ - } - } + /* Check the ACL (we can make this better later) */ + if(channel->acl) + { + if(!apr_ipsubnet_test( channel->acl, remotesa)) + { + debug_msg("Ignoring data from %s\n", remoteip); + return; + } + } - if(!strcasecmp(protocol, "xdr")) - { - XDR x; - Ganglia_message msg; - Ganglia_host_data *hostdata = NULL; - - /* Create the XDR receive stream */ - xdrmem_create(&x, buf, max_udp_message_len, XDR_DECODE); - - /* Flush the data... */ - memset( &msg, 0, sizeof(Ganglia_message)); - - /* Read the gangliaMessage from the stream */ - if(!xdr_Ganglia_message(&x, &msg)) - { - continue; - } - - /* Process the host information and get the Ganglia_host_data. - * We call this _after_ we process the content of the message - * because the newer message format allows for proxy information - * to be sent. NOTE: The ACL test above looks at the IP header - * and not the proxy information provided. */ - hostdata = Ganglia_host_data_get( remoteip, remotesa, &msg); - if(!hostdata) - { - /* Processing of this message is finished ... */ - xdr_free((xdrproc_t)xdr_Ganglia_message, (char *)&msg); - continue; - } + /* Create the XDR receive stream */ + xdrmem_create(&x, buf, max_udp_message_len, XDR_DECODE); - /* Save the message from this particular host */ - Ganglia_message_save( hostdata, &msg ); + /* Flush the data... */ + memset( &msg, 0, sizeof(Ganglia_message)); -#if 0 - /* Save the message to the hostdata metric hash */ - old_metric = Ganglia_old_metric_get( msg.id ); - if(old_metric) - { - /* Move this data into a newer format (later) */ - debug_msg("%s\t(%s)\t=>\t%s", hostdata->ip, hostdata->hostname, old_metric->name); - } -#endif + /* Read the gangliaMessage from the stream */ + if(!xdr_Ganglia_message(&x, &msg)) + { + return; + } - /* - xdr_free((xdrproc_t)xdr_Ganglia_message, (char *)&msg); - */ - - /* If I want to find out how much data I decoded - decoded = xdr_getpos(&x); */ - } - } + hostdata = Ganglia_host_data_get( remoteip, remotesa, &msg); + if(!hostdata) + { + /* Processing of this message is finished ... */ + xdr_free((xdrproc_t)xdr_Ganglia_message, (char *)&msg); + return; } + + /* Save the message from this particular host */ + Ganglia_message_save( hostdata, &msg ); } static void @@ -591,7 +664,7 @@ setup_udp_send_array( void ) for(i = 0; i< num_udp_send_channels; i++) { cfg_t *udp_send_channel; - char *mcast_join, *mcast_if, *protocol, *ip; + char *mcast_join, *mcast_if, *ip; int port; apr_socket_t *socket = NULL; apr_pool_t *pool = NULL; @@ -601,14 +674,12 @@ setup_udp_send_array( void ) mcast_join = cfg_getstr( udp_send_channel, "mcast_join" ); mcast_if = cfg_getstr( udp_send_channel, "mcast_if" ); port = cfg_getint( udp_send_channel, "port"); - protocol = cfg_getstr( udp_send_channel, "protocol"); - debug_msg("udp_send_channel mcast_join=%s mcast_if=%s ip=%s port=%d protocol=%s\n", + debug_msg("udp_send_channel mcast_join=%s mcast_if=%s ip=%s port=%d\n", mcast_join? mcast_join:"NULL", mcast_if? mcast_if:"NULL", ip, - port, - protocol? protocol:"NULL"); + port); /* Create a subpool */ apr_pool_create(&pool, global_context); @@ -642,82 +713,6 @@ setup_udp_send_array( void ) } } -static void -setup_tcp_accept_pollset( void ) -{ - apr_status_t status; - int i, num_tcp_accept_channels = cfg_size( config_file, "tcp_accept_channel"); - - /* Create my TCP accept pollset */ - apr_pollset_create(&tcp_accept_pollset, num_tcp_accept_channels, global_context, 0); - - /* Create my TCP accept ACL array */ - tcp_accept_acl_array = apr_array_make( global_context, num_tcp_accept_channels, - sizeof(apr_ipsubnet_t *)); - - for(i=0; i< num_tcp_accept_channels; i++) - { - cfg_t *tcp_accept_channel = cfg_getnsec( config_file, "tcp_accept_channel", i); - char *bindaddr, *protocol, *allow_ip, *allow_mask, *interface; - int port; - apr_socket_t *socket = NULL; - apr_ipsubnet_t *ipsub = NULL; - apr_pollfd_t socket_pollfd; - apr_pool_t *pool = NULL; - - port = cfg_getint( tcp_accept_channel, "port"); - bindaddr = cfg_getstr( tcp_accept_channel, "bind"); - protocol = cfg_getstr( tcp_accept_channel, "protocol"); - allow_ip = cfg_getstr( tcp_accept_channel, "allow_ip"); - allow_mask = cfg_getstr( tcp_accept_channel, "allow_mask"); - interface = cfg_getstr( tcp_accept_channel, "interface"); - - debug_msg("tcp_accept_channel bind=%s port=%d protocol=%s\n", - bindaddr? bindaddr: "NULL", port, - protocol? protocol:"NULL"); - - /* Create a subpool context */ - apr_pool_create(&pool, global_context); - - /* Create the socket for the channel */ - socket = create_tcp_server(pool, port, bindaddr, interface); - if(!socket) - { - fprintf(stderr,"Unable to create tcp_accept_channel. Exiting.\n"); - exit(1); - } - - /* Build the socket poll file descriptor structure */ - socket_pollfd.desc_type = APR_POLL_SOCKET; - socket_pollfd.reqevents = APR_POLLIN; - socket_pollfd.desc.s = socket; - socket_pollfd.client_data = protocol; - - /* Add the socket to the pollset */ - status = apr_pollset_add(tcp_accept_pollset, &socket_pollfd); - if(status != APR_SUCCESS) - { - fprintf(stderr,"Failed to add socket to pollset. Exiting.\n"); - exit(1); - } - - /* Save the ACL information */ - if(allow_ip) - { - status = apr_ipsubnet_create(&ipsub, allow_ip, allow_mask, pool); - if(status != APR_SUCCESS) - { - fprintf(stderr,"Unable to build ACL for ip=%s mask=%s. Exiting.\n", - allow_ip, allow_mask); - exit(1); - } - } - - /* ipsub of NULL means no acl in effect */ - *(apr_ipsubnet_t **)apr_array_push(tcp_accept_acl_array) = ipsub; - } -} - static apr_status_t print_xml_header( apr_socket_t *client ) { @@ -785,9 +780,9 @@ print_host_start( apr_socket_t *client, Ganglia_host_data *hostinfo) (int)(hostinfo->last_heard_from / APR_USEC_PER_SEC), tn, 20, /*tmax for now is always 20 */ - 0 /*dmax*/, - "unspecified", /*location*/ - 0 /*gmond_started*/); + 0 /* TODO: (config option) dmax*/, + "unspecified", /* TODO: (location) location*/ + 0 /*TODO: (heartbeat) gmond_started*/); return apr_send(client, hostxml, &len); } @@ -838,12 +833,14 @@ host_metric_value( Ganglia_25metric *metric, Ganglia_message *message ) apr_snprintf(value, 1024, metric->fmt, message->Ganglia_message_u.u_short); return value; case GANGLIA_VALUE_SHORT: + /* For right now.. there are no metrics which are signed shorts... use u_short */ apr_snprintf(value, 1024, metric->fmt, message->Ganglia_message_u.u_short); return value; case GANGLIA_VALUE_UNSIGNED_INT: apr_snprintf(value, 1024, metric->fmt, message->Ganglia_message_u.u_int); return value; case GANGLIA_VALUE_INT: + /* For right now.. there are no metric which are signed ints... use u_int */ apr_snprintf(value, 1024, metric->fmt, message->Ganglia_message_u.u_int); return value; case GANGLIA_VALUE_FLOAT: @@ -865,7 +862,7 @@ print_host_metric( apr_socket_t *client, Ganglia_metric_data *data ) apr_size_t len; apr_time_t now; - metric = Ganglia_25metric_bykey( data->message.id ); + metric = Ganglia_25metric_bykey( data? data->message.id: -1 ); if(!metric) return APR_SUCCESS; @@ -893,122 +890,130 @@ print_host_end( apr_socket_t *client) } static void -poll_tcp_accept_channels(apr_interval_time_t timeout) +process_tcp_accept_channel(const apr_pollfd_t *desc) { apr_status_t status; - const apr_pollfd_t *descs = NULL; - apr_int32_t num = 0; + apr_hash_index_t *hi, *metric_hi; + void *val; + apr_socket_t *client, *server; + apr_sockaddr_t *remotesa = NULL; + char remoteip[256]; + apr_pool_t *client_context = NULL; + Ganglia_channel *channel; + + server = desc->desc.s; + /* We could also use the apr_socket_data_get/set() functions + * to have per socket user data .. see APR docs */ + channel = desc->client_data; + + /* Create a context for the client connection */ + apr_pool_create(&client_context, global_context); + + /* Accept the connection */ + status = apr_accept(&client, server, client_context); + if(status != APR_SUCCESS) + { + goto close_accept_socket; + } + + apr_socket_addr_get(&remotesa, APR_REMOTE, client); + /* This function is in ./lib/apr_net.c and not APR. The + * APR counterpart is apr_sockaddr_ip_get() but we don't + * want to malloc memory evertime we call this */ + apr_sockaddr_ip_buffer_get(remoteip, 256, remotesa); - /* Poll for data with given timeout */ - status = apr_pollset_poll(tcp_accept_pollset, timeout, &num, &descs); + /* Check the ACL (we can make this better later) */ + if(channel->acl) + { + if(!apr_ipsubnet_test( channel->acl, remotesa)) + { + debug_msg("Ignoring connection from %s\n", remoteip); + goto close_accept_socket; + } + } + + /* Print the DTD, GANGLIA_XML and CLUSTER tags */ + status = print_xml_header(client); if(status != APR_SUCCESS) - return; + goto close_accept_socket; - if(num>0) + /* Walk the host hash */ + for(hi = apr_hash_first(client_context, hosts); + hi; + hi = apr_hash_next(hi)) { - int i; + apr_hash_this(hi, NULL, NULL, &val); + status = print_host_start(client, (Ganglia_host_data *)val); + if(status != APR_SUCCESS) + { + goto close_accept_socket; + } - /* We have data to read */ - for(i=0; i< num; i++) + /* Send the metric info for this particular host */ + for(metric_hi = apr_hash_first(client_context, ((Ganglia_host_data *)val)->metrics); + metric_hi; + metric_hi = apr_hash_next(metric_hi)) { - apr_socket_t *client, *server; - apr_sockaddr_t *remotesa = NULL; - char *protocol, remoteip[256]; - apr_ipsubnet_t *ipsub; - apr_pool_t *client_context = NULL; - - server = descs[i].desc.s; - /* We could also use the apr_socket_data_get/set() functions - * to have per socket user data .. see APR docs */ - protocol = descs[i].client_data; - - /* Create a context for the client connection */ - apr_pool_create(&client_context, global_context); - - /* Accept the connection */ - status = apr_accept(&client, server, client_context); - if(status != APR_SUCCESS) + void *metric; + apr_hash_this(metric_hi, NULL, NULL, &metric); + + /* Print each of the metrics for a host ... */ + if(print_host_metric(client, metric) != APR_SUCCESS) { goto close_accept_socket; } + } - apr_socket_addr_get(&remotesa, APR_REMOTE, client); - /* This function is in ./lib/apr_net.c and not APR. The - * APR counterpart is apr_sockaddr_ip_get() but we don't - * want to malloc memory evertime we call this */ - apr_sockaddr_ip_buffer_get(remoteip, 256, remotesa); + /* Close the host tag */ + status = print_host_end(client); + if(status != APR_SUCCESS) + { + goto close_accept_socket; + } + } - /* Check the ACL (we can make this better later) */ - ipsub = ((apr_ipsubnet_t **)(tcp_accept_acl_array->elts))[i]; - if(ipsub) - { - if(!apr_ipsubnet_test( ipsub, remotesa)) - { - debug_msg("Ignoring connection from %s\n", remoteip); - goto close_accept_socket; - } - } + /* Close the CLUSTER and GANGLIA_XML tags */ + print_xml_footer(client); - /* At this point send data over the socket */ - if(!strcasecmp(protocol, "xml")) - { - apr_status_t status; - apr_hash_index_t *hi, *metric_hi; - void *val; - - /* Print the DTD, GANGLIA_XML and CLUSTER tags */ - status = print_xml_header(client); - if(status != APR_SUCCESS) - goto close_accept_socket; - - /* Walk the host hash */ - for(hi = apr_hash_first(client_context, hosts); - hi; - hi = apr_hash_next(hi)) - { - apr_hash_this(hi, NULL, NULL, &val); - status = print_host_start(client, (Ganglia_host_data *)val); - if(status != APR_SUCCESS) - { - goto close_accept_socket; - } - - /* Send the metric info for this particular host */ - for(metric_hi = apr_hash_first(client_context, ((Ganglia_host_data *)val)->metrics); - metric_hi; - metric_hi = apr_hash_next(metric_hi)) - { - void *metric; - apr_hash_this(metric_hi, NULL, NULL, &metric); - - /* Print each of the metrics for a host ... */ - if(print_host_metric(client, metric) != APR_SUCCESS) - { - goto close_accept_socket; - } - } + /* Close down the accepted socket */ +close_accept_socket: + apr_shutdown(client, APR_SHUTDOWN_READ); + apr_socket_close(client); + apr_pool_destroy(client_context); +} - /* Close the host tag */ - status = print_host_end(client); - if(status != APR_SUCCESS) - { - goto close_accept_socket; - } - } - /* Close the CLUSTER and GANGLIA_XML tags */ - print_xml_footer(client); - } +static void +poll_listen_channels( apr_interval_time_t timeout ) +{ + apr_status_t status; + const apr_pollfd_t *descs = NULL; + apr_int32_t num = 0; + apr_int32_t i; - /* Close down the accepted socket */ - close_accept_socket: - apr_shutdown(client, APR_SHUTDOWN_READ); - apr_socket_close(client); - apr_pool_destroy(client_context); - } + /* Poll for incoming data */ + status = apr_pollset_poll(listen_channels, timeout, &num, &descs); + if(status != APR_SUCCESS) + return; + + for(i = 0; i< num ; i++) + { + Ganglia_channel *channel = descs[i].client_data; + switch( channel->type ) + { + case UDP_RECV_CHANNEL: + process_udp_recv_channel(descs+i); + break; + case TCP_ACCEPT_CHANNEL: + process_tcp_accept_channel(descs+i); + break; + default: + continue; + } } } + /* This function will send a datagram to every udp_send_channel specified */ static int udp_send_message( char *buf, int len ) @@ -1042,6 +1047,12 @@ tcp_send_message( char *buf, int len ) return 0; } +static int +send_message( char *buf, int len ) +{ + return udp_send_message( buf, len ) + tcp_send_message( buf, len ); +} + static Ganglia_metric_callback * Ganglia_metric_cb_define( char *name, g_val_t (*cb)(void)) { @@ -1054,10 +1065,48 @@ Ganglia_metric_cb_define( char *name, g_val_t (*cb)(void)) return NULL; metric->cb = cb; + apr_hash_set( metric_callbacks, metric->name, APR_HASH_KEY_STRING, metric); return metric; } +#if 0 +g_val_t +gexec_func ( void ) +{ + g_val_t val; + + if( gmond_config.no_gexec || ( SUPPORT_GEXEC == 0 ) ) + snprintf(val.str, MAX_G_STRING_SIZE, "%s", "OFF"); + else + snprintf(val.str, MAX_G_STRING_SIZE, "%s", "ON"); + + return val; +} +#endif + +g_val_t +heartbeat_func( void ) +{ + g_val_t val; + + val.uint32 = started / APR_USEC_PER_SEC; + debug_msg("my start_time is %d\n", val.uint32); + return val; +} + +#if 0 +g_val_t +location_func(void) +{ + g_val_t val; + + strncpy(val.str, gmond_config.location, MAX_G_STRING_SIZE); + debug_msg("my location is %s", val.str); + return val; +} +#endif + /* This function imports the metrics from libmetrics right now but in the future * we could easily do this via DSO. */ static void @@ -1102,39 +1151,276 @@ setup_metric_callbacks( void ) Ganglia_metric_cb_define("mem_buffers", mem_buffers_func); Ganglia_metric_cb_define("mem_cached", mem_cached_func); Ganglia_metric_cb_define("swap_free", swap_free_func); + + Ganglia_metric_cb_define("heartbeat", heartbeat_func); + + /* Add platform specific metrics here... */ } - -int -process_collection_groups( void ) + +void +setup_collection_groups( void ) { int i, num_collection_groups = cfg_size( config_file, "collection_group" ); + + /* Create the collection group array */ + collection_groups = apr_array_make( global_context, num_collection_groups, + sizeof(Ganglia_collection_group *)); - for(i=0; i< num_collection_groups; i++) + for(i = 0; i < num_collection_groups; i++) { int j, num_metrics; + cfg_t *group_conf; + Ganglia_collection_group *group = apr_pcalloc( global_context, + sizeof(Ganglia_collection_group)); + if(!group) + { + fprintf(stderr,"Unable to malloc memory for collection group. Exiting.\n"); + exit(1); + } - cfg_t *group = cfg_getnsec( config_file, "collection_group", i); - char *name = cfg_getstr( group, "name"); - num_metrics = cfg_size( group, "metric" ); + group_conf = cfg_getnsec( config_file, "collection_group", i); + group->once = cfg_getbool( group_conf, "collect_once"); + group->collect_every = cfg_getint( group_conf, "collect_every"); + group->time_threshold = cfg_getint( group_conf, "time_threshold"); + /* Set to collect and send immediately */ + group->next_collect = 0; + group->next_send = 0; + + num_metrics = cfg_size( group_conf, "metric" ); + group->metric_array = apr_array_make(global_context, num_metrics, + sizeof(Ganglia_metric_callback *)); for(j=0; j< num_metrics; j++) { - cfg_t *metric = cfg_getnsec( group, "metric", j ); + cfg_t *metric = cfg_getnsec( group_conf, "metric", j ); + char *name = cfg_getstr ( metric, "name"); + float value_threshold = cfg_getfloat( metric, "value_threshold"); + + Ganglia_metric_callback *metric_cb = (Ganglia_metric_callback *) + apr_hash_get( metric_callbacks, name, APR_HASH_KEY_STRING ); + Ganglia_25metric *metric_info = Ganglia_25metric_byname(name); + + if(!metric_cb) + { + fprintf(stderr,"Unable to collect metric '%s' on this platform. Exiting.\n", name); + exit(1); + } + + if(!metric_info) + { + fprintf(stderr,"Unable to send metric '%s' (not in protocol.x). Exiting.\n", name); + exit(1); + } + + /* This sets the key for this particular metric. + * The value is set by the callback function later */ + metric_cb->msg.id = metric_info->key; + + /* Save the location of information about this particular metric */ + metric_cb->info = metric_info; + + /* Set the value threshold for this particular metric */ + metric_cb->value_threshold = value_threshold; + + if(group->once) + { + /* If this metric will only be collected once, run it now at setup... */ + metric_cb->now = metric_cb->cb(); + memset( &(metric_cb->last), 0, sizeof(g_val_t)); + } + else + { + memset( &(metric_cb->now), 0, sizeof(g_val_t)); + memset( &(metric_cb->last), 0, sizeof(g_val_t)); + } + + *(Ganglia_metric_callback **)apr_array_push(group->metric_array) = metric_cb; + } + + /* Save the collection group the collection group array */ + *(Ganglia_collection_group **)apr_array_push(collection_groups) = group; + } +} + +/* TODO: It might be necessary in the future to use a heap for the collection groups. + * Running through an array should suffice for now */ +apr_time_t +process_collection_groups( apr_time_t now ) +{ + int i,j; + apr_time_t next = 0; + + /* Run through each collection group and collect any data that needs collecting... */ + for(i=0; i< collection_groups->nelts; i++) + { + Ganglia_collection_group *group = ((Ganglia_collection_group **)(collection_groups->elts))[i]; + + debug_msg("process_collection_group #%d", i); + + if(group->once) + { + /* This group is only collected once at setup time */ + debug_msg("\tgroup #%d is only collected once. skipping.", i); + continue; + } + + if( group->next_collect <= now ) + { + debug_msg("\tgroup #%d needs to be collected now", i); + /* This group needs to be collected now */ + for(j=0; j< group->metric_array->nelts; j++) + { + Ganglia_metric_callback *cb = ((Ganglia_metric_callback **)(group->metric_array->elts))[j]; + + debug_msg("\tgroup #%d metric '%s' being collected now", i, cb->name); + cb->last = cb->now; + cb->now = cb->cb(); + + /* Check the value threshold. If passed.. set this group to send immediately. */ + if( cb->value_threshold >= 0.0 ) + { + debug_msg("\tmetric '%s' has value_threshold %f", cb->name, cb->value_threshold); + switch(cb->info->type) + { + case GANGLIA_VALUE_UNKNOWN: + case GANGLIA_VALUE_STRING: + /* do nothing for non-numeric data */ + break; + case GANGLIA_VALUE_UNSIGNED_SHORT: + if( abs( cb->last.uint16 - cb->now.uint16 ) >= cb->value_threshold ) + group->next_send = 0; /* send immediately */ + break; + case GANGLIA_VALUE_SHORT: + if( abs( cb->last.int16 - cb->now.int16 ) >= cb->value_threshold ) + group->next_send = 0; /* send immediately */ + break; + case GANGLIA_VALUE_UNSIGNED_INT: + if( abs( cb->last.uint32 - cb->now.uint32 ) >= cb->value_threshold ) + group->next_send = 0; /* send immediately */ + break; + case GANGLIA_VALUE_INT: + if( abs( cb->last.int32 - cb->now.int32 ) >= cb->value_threshold ) + group->next_send = 0; /* send immediately */ + break; + case GANGLIA_VALUE_FLOAT: + if( abs( cb->last.f - cb->now.f ) >= cb->value_threshold ) + group->next_send = 0; /* send immediately */ + break; + case GANGLIA_VALUE_DOUBLE: + if( abs( cb->last.d - cb->now.d ) >= cb->value_threshold ) + group->next_send = 0; /* send immediately */ + break; + default: + break; + } + + } + + } + /* Set the next time this group should be collected */ + group->next_collect = now + (group->collect_every * APR_USEC_PER_SEC); + if(debug_level) + { + char timestr[APR_CTIME_LEN]; + apr_ctime(timestr, group->next_collect); + debug_msg("\tcollection_group #%d next collection at %s", i, timestr); + } + } + else + { + debug_msg("\tcollection_group #%d does not need to be collected at this time.",i); + } + } + + + /* TODO: this would be done better with a heap... + * Send any groups that need to be sent and + * Find out exactly when we need to run process_collection_groups again */ + for(i=0; i< collection_groups->nelts; i++) + { + Ganglia_collection_group *group = ((Ganglia_collection_group **)(collection_groups->elts))[i]; + + /* Send any groups that need to be sent now */ + if( group->next_send <= now ) + { + debug_msg("\tcollection_group #%d will send data", i); + /* This group needs to be sent */ + for(j=0; j< group->metric_array->nelts; j++) + { + XDR x; + int len, errors; + char metricmsg[max_udp_message_len]; + Ganglia_metric_callback *cb = ((Ganglia_metric_callback **)(group->metric_array->elts))[j]; + + /* Build the message */ + switch(cb->info->type) + { + case GANGLIA_VALUE_UNKNOWN: + /* The 2.5.x protocol doesn't allow for unknown values. :( Do nothing. */ + continue; + case GANGLIA_VALUE_STRING: + cb->msg.Ganglia_message_u.str = cb->now.str; + break; + case GANGLIA_VALUE_UNSIGNED_SHORT: + cb->msg.Ganglia_message_u.u_short = cb->now.uint16; + break; + case GANGLIA_VALUE_SHORT: + /* TODO: currently there are no signed short values in protocol.x + * As soon as we add one, we need to implement it here... */ + break; + case GANGLIA_VALUE_UNSIGNED_INT: + cb->msg.Ganglia_message_u.u_int = cb->now.uint32; + break; + case GANGLIA_VALUE_INT: + /* TODO: currently there are no signed int value in protocol.x + * As soon as we add one, we need to implement it here... */ + break; + case GANGLIA_VALUE_FLOAT: + cb->msg.Ganglia_message_u.f = cb->now.f; + break; + case GANGLIA_VALUE_DOUBLE: + cb->msg.Ganglia_message_u.d = cb->now.d; + break; + default: + continue; + } - /* Process the data for this metric */ - + /* Send the message */ + xdrmem_create(&x, metricmsg, max_udp_message_len, XDR_ENCODE); + xdr_Ganglia_message(&x, &(cb->msg)); + len = xdr_getpos(&x); + errors = send_message( metricmsg, len ); + debug_msg("\tsent message of length %d with %d errors", len, errors); + if(!errors) + { + /* If the message send ok. Schedule the next time threshold. */ + group->next_send = now + group->time_threshold * APR_USEC_PER_SEC; + } + } + } + /* Find the next time we need to run process_collection_group() */ + if(!next) + { + next = group->next_collect; + } + else + { + if(group->next_collect < next ) + { + next = group->next_collect; + } } } - return 2; + /* If next is 0... drop back in here in 60 seconds (no loops) */ + return next? next: apr_time_now() + 60 * APR_USEC_PER_SEC; } int main ( int argc, char *argv[] ) { - apr_interval_time_t now, stop; - int next_collection; + apr_time_t now, next_collection; /* Mark the time this gmond started */ started = apr_time_now(); @@ -1167,43 +1453,37 @@ main ( int argc, char *argv[] ) if(!deaf) { - setup_udp_recv_pollset(); - setup_tcp_accept_pollset(); + setup_listen_channels_pollset(); } if(!mute) { setup_metric_callbacks(); + setup_collection_groups(); setup_udp_send_array(); } /* Create the host hash table */ hosts = apr_hash_make( global_context ); - next_collection = 0; - for(;;) + for(next_collection = 0;;) { - now = apr_time_now(); - stop = now + (next_collection* APR_USEC_PER_SEC); + now = apr_time_now(); + /* Read data until we need to collect/write data */ - for(; now < stop ;) + if(!deaf) { - if(!deaf) + for(; mute || now < next_collection;) { - poll_tcp_accept_channels(0); - poll_udp_recv_channels(stop - now); - } - now = apr_time_now(); + /* TODO: merge all receive channels into one poll */ + poll_listen_channels(next_collection - now); + now = apr_time_now(); + } } if(!mute) { - next_collection = process_collection_groups(); - } - else - { - next_collection = 3600; /* if we're mute. - set the default timeout large...*/ + next_collection = process_collection_groups( now ); } } diff --git a/monitor-core/gmond/gmond.conf b/monitor-core/gmond/gmond.conf index 551c0b9f2..f18ba57f3 100644 --- a/monitor-core/gmond/gmond.conf +++ b/monitor-core/gmond/gmond.conf @@ -3,32 +3,44 @@ behavior { user = nobody } udp_send_channel { - ip = 239.2.11.71 + ip = 127.0.0.1 port = 8649 } udp_recv_channel { port = 8649 } +tcp_accept_channel { + port = 8649 +} +collection_group { + collect_once = true + time_threshold = 20 + metric { + name = "heartbeat" + } +} collection_group { - name = "cpu_stat" + collect_every = 2 + time_threshold = 60 metric { name = "cpu_user" - absolute_minimum = 0 - absolute_maximum = 100 } metric { - name = "cpu_sys" - absolute_minimum = 0 - absolute_maximum = 100 + name = "cpu_system" } metric { name = "cpu_idle" - absolute_minimum = 0 - absolute_maximum = 100 } metric { name = "cpu_nice" - absolute_minimum = 0 - absolute_maximum = 100 } } +collection_group { + collect_every = 5 + metric { + name = "mem_free" + } + metric { + name = "mem_total" + } +} diff --git a/monitor-core/lib/apr_net.c b/monitor-core/lib/apr_net.c index 9caaff230..26f0b1cc6 100644 --- a/monitor-core/lib/apr_net.c +++ b/monitor-core/lib/apr_net.c @@ -51,14 +51,14 @@ APR_DECLARE(apr_status_t) apr_sockaddr_ip_buffer_get(char *addr, int len, } static apr_socket_t * -create_net_client(apr_pool_t *context, int type, char *ipaddr, apr_port_t port) +create_net_client(apr_pool_t *context, int type, char *host, apr_port_t port) { apr_sockaddr_t *remotesa = NULL; apr_socket_t *sock = NULL; apr_status_t status; int family = APR_UNSPEC; - status = apr_sockaddr_info_get(&remotesa, ipaddr, APR_UNSPEC, port, 0, context); + status = apr_sockaddr_info_get(&remotesa, host, APR_UNSPEC, port, 0, context); if(status!= APR_SUCCESS) { return NULL; @@ -84,9 +84,9 @@ create_net_client(apr_pool_t *context, int type, char *ipaddr, apr_port_t port) } apr_socket_t * -create_udp_client(apr_pool_t *context, char *ipaddr, apr_port_t port) +create_udp_client(apr_pool_t *context, char *host, apr_port_t port) { - return create_net_client(context, SOCK_DGRAM, ipaddr, port); + return create_net_client(context, SOCK_DGRAM, host, port); } static apr_socket_t *