diff options
author | Nathan Kinder <nkinder@redhat.com> | 2005-02-25 22:47:13 +0000 |
---|---|---|
committer | Nathan Kinder <nkinder@redhat.com> | 2005-02-25 22:47:13 +0000 |
commit | 791b7e016853125fc759ee750959e6ce121533c0 (patch) | |
tree | 5a3d5f877435bd7621b1f85902088eaaf00b0b48 /ldap/servers/snmp | |
parent | 6703f63899ba102659292134a5abccc690fecae6 (diff) | |
download | ds-791b7e016853125fc759ee750959e6ce121533c0.tar.gz ds-791b7e016853125fc759ee750959e6ce121533c0.tar.xz ds-791b7e016853125fc759ee750959e6ce121533c0.zip |
New net-snmp subagent
Diffstat (limited to 'ldap/servers/snmp')
-rw-r--r-- | ldap/servers/snmp/ldap-agent.c | 678 | ||||
-rw-r--r-- | ldap/servers/snmp/ldap-agent.h | 146 | ||||
-rw-r--r-- | ldap/servers/snmp/main.c | 367 |
3 files changed, 1191 insertions, 0 deletions
diff --git a/ldap/servers/snmp/ldap-agent.c b/ldap/servers/snmp/ldap-agent.c new file mode 100644 index 00000000..a8060e8c --- /dev/null +++ b/ldap/servers/snmp/ldap-agent.c @@ -0,0 +1,678 @@ +#include <stdio.h> +#include <time.h> +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> +#include <net-snmp/library/snmp_assert.h> + +#include "ldap-agent.h" + +static netsnmp_handler_registration *ops_handler = NULL; +static netsnmp_handler_registration *entries_handler = NULL; +static netsnmp_handler_registration *entity_handler = NULL; +static netsnmp_table_array_callbacks ops_cb; +static netsnmp_table_array_callbacks entries_cb; +static netsnmp_table_array_callbacks entity_cb; +extern server_instance *server_head; + +/* Set table oids */ +oid dsOpsTable_oid[] = { dsOpsTable_TABLE_OID }; +size_t dsOpsTable_oid_len = OID_LENGTH(dsOpsTable_oid); +oid dsEntriesTable_oid[] = { dsEntriesTable_TABLE_OID }; +size_t dsEntriesTable_oid_len = OID_LENGTH(dsEntriesTable_oid); +oid dsEntityTable_oid[] = {dsEntityTable_TABLE_OID }; +size_t dsEntityTable_oid_len = OID_LENGTH(dsEntityTable_oid); + +/* Set trap oids */ +oid snmptrap_oid[] = { snmptrap_OID }; +size_t snmptrap_oid_len = OID_LENGTH(snmptrap_oid); +oid enterprise_oid[] = { enterprise_OID }; +size_t enterprise_oid_len = OID_LENGTH(enterprise_oid); + +/************************************************************ + * init_ldap_agent + * + * Initializes the agent and populates the stats table + * with initial data. + */ +void +init_ldap_agent(void) +{ + server_instance *serv_p = NULL; + stats_table_context *new_row = NULL; + int err; + int stats_hdl = -1; + + /* Define and create the table */ + initialize_stats_table(); + + /* Initialize data for each server in conf file */ + for (serv_p = server_head; serv_p != NULL; serv_p = serv_p->next) { + /* Check if this row already exists. */ + if ((new_row = stats_table_find_row(serv_p->port)) == NULL) { + /* Create a new row */ + if ((new_row = stats_table_create_row(serv_p->port)) != NULL) { + /* Set pointer for entity table */ + new_row->entity_tbl = serv_p; + + /* Set previous state of server to unknown */ + serv_p->server_state = STATE_UNKNOWN; + + /* Insert new row into the table */ + snmp_log(LOG_DEBUG, "Inserting row for server: %d\n", serv_p->port); + CONTAINER_INSERT(ops_cb.container, new_row); + } else { + /* error during malloc of row */ + snmp_log(LOG_ERR, "Error creating row for server: %d\n", + serv_p->port); + } + } + } + + /* Force load data into stats table */ + load_stats_table(NULL, NULL); +} + +/************************************************************ + * initialize_stats_table + * + * Initializes the stats table by defining its contents, + * how it's structured, and registering callbacks. + */ +void +initialize_stats_table(void) +{ + netsnmp_table_registration_info *ops_table_info = NULL; + netsnmp_table_registration_info *entries_table_info = NULL; + netsnmp_table_registration_info *entity_table_info = NULL; + netsnmp_cache *stats_table_cache = NULL; + + if (ops_handler || entries_handler || entity_handler) { + snmp_log(LOG_ERR, "initialize_stats_table called more than once.\n"); + return; + } + + memset(&ops_cb, 0x00, sizeof(ops_cb)); + memset(&entries_cb, 0x00, sizeof(entries_cb)); + memset(&entity_cb, 0x00, sizeof(entity_cb)); + + /* create table structures */ + ops_table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info); + entries_table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info); + entity_table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info); + + /* create handlers */ + ops_handler = netsnmp_create_handler_registration("dsOpsTable", + netsnmp_table_array_helper_handler, + dsOpsTable_oid, + dsOpsTable_oid_len, + HANDLER_CAN_RONLY); + entries_handler = netsnmp_create_handler_registration("dsEntriesTable", + netsnmp_table_array_helper_handler, + dsEntriesTable_oid, + dsEntriesTable_oid_len, + HANDLER_CAN_RONLY); + entity_handler = netsnmp_create_handler_registration("dsEntityTable", + netsnmp_table_array_helper_handler, + dsEntityTable_oid, + dsEntityTable_oid_len, + HANDLER_CAN_RONLY); + + if (!ops_handler || !entries_handler || !entity_handler || + !ops_table_info || !entries_table_info || !entity_table_info) { + /* malloc failed */ + snmp_log(LOG_ERR, "malloc failed in initialize_stats_table\n"); + return; + } + + /* define table structures */ + netsnmp_table_helper_add_index(ops_table_info, ASN_INTEGER); + netsnmp_table_helper_add_index(entries_table_info, ASN_INTEGER); + netsnmp_table_helper_add_index(entity_table_info, ASN_INTEGER); + + ops_table_info->min_column = dsOpsTable_COL_MIN; + ops_table_info->max_column = dsOpsTable_COL_MAX; + entries_table_info->min_column = dsEntriesTable_COL_MIN; + entries_table_info->max_column = dsEntriesTable_COL_MAX; + entity_table_info->min_column = dsEntityTable_COL_MIN; + entity_table_info->max_column = dsEntityTable_COL_MAX; + + /* + * Define callbacks and the container. We only use one container that + * all of the tables use. + */ + ops_cb.get_value = dsOpsTable_get_value; + ops_cb.container = netsnmp_container_find("dsOpsTable_primary:" + "dsOpsTable:" "table_container"); + entries_cb.get_value = dsEntriesTable_get_value; + entries_cb.container = ops_cb.container; + entity_cb.get_value = dsEntityTable_get_value; + entity_cb.container = ops_cb.container; + + /* registering the tables with the master agent */ + netsnmp_table_container_register(ops_handler, ops_table_info, &ops_cb, + ops_cb.container, 1); + netsnmp_table_container_register(entries_handler, entries_table_info, &entries_cb, + entries_cb.container, 1); + netsnmp_table_container_register(entity_handler, entity_table_info, &entity_cb, + entity_cb.container, 1); + + /* Setup cache for auto reloading of stats */ + stats_table_cache = netsnmp_cache_create(CACHE_REFRESH_INTERVAL, load_stats_table, + NULL, dsOpsTable_oid, dsOpsTable_oid_len); + stats_table_cache->flags |= NETSNMP_CACHE_DONT_FREE_EXPIRED; + stats_table_cache->flags |= NETSNMP_CACHE_DONT_AUTO_RELEASE; + stats_table_cache->flags |= NETSNMP_CACHE_AUTO_RELOAD; + netsnmp_inject_handler(ops_handler, netsnmp_cache_handler_get(stats_table_cache)); +} + +/************************************************************ + * stats_table_create_row + * + * Creates a new table row using the supplied port number as + * the index, then returns a pointer to the new row. + */ +stats_table_context * +stats_table_create_row(unsigned long portnum) +{ + netsnmp_index index; + stats_table_context *ctx = SNMP_MALLOC_TYPEDEF(stats_table_context); + oid *index_oid = (oid *)malloc(sizeof(oid) * MAX_OID_LEN); + + /* Create index using port number */ + index_oid[0] = portnum; + index.oids = index_oid; + index.len = 1; + + /* Copy index into row structure */ + if (ctx && index_oid) { + memcpy(&ctx->index, &index, sizeof(index)); + return ctx; + } else { + /* Error during malloc */ + snmp_log(LOG_ERR, "malloc failed in stats_table_create_row\n"); + return NULL; + } +} + +/************************************************************ + * stats_table_find_row + * + * Searches for a row by the port number. Returns NULL if + * the row doesn't exist. + */ +stats_table_context * +stats_table_find_row(unsigned long portnum) +{ + netsnmp_index index; + oid index_oid[MAX_OID_LEN]; + + index_oid[0] = portnum; + index.oids = index_oid; + index.len = 1; + + return (stats_table_context *) + CONTAINER_FIND(ops_cb.container, &index); +} + +/************************************************************ + * load_stats_table + * + * Reloads the stats into the table. This is called + * automatically from the cache handler. This function + * does not reload the entity table since it's static + * information. We also check if any traps need to + * be sent here. + */ +int +load_stats_table(netsnmp_cache *cache, void *foo) +{ + server_instance *serv_p = NULL; + stats_table_context *ctx = NULL; + netsnmp_variable_list *vars = NULL; + time_t previous_start; + int previous_state; + int stats_hdl = -1; + int err; + + snmp_log(LOG_DEBUG, "Reloading stats.\n"); + + /* Initialize data for each server in conf file */ + for (serv_p = server_head; serv_p != NULL; serv_p = serv_p->next) { + if ((ctx = stats_table_find_row(serv_p->port)) != NULL) { + /* Save previous state of the server to + * see if a trap needs to be sent */ + previous_state = serv_p->server_state; + previous_start = ctx->hdr_tbl.startTime; + + snmp_log(LOG_DEBUG, "Opening stats file (%s) for server: %d\n", + serv_p->stats_file, serv_p->port); + + /* Open the stats file */ + if ((err = agt_mopen_stats(serv_p->stats_file, O_RDONLY, &stats_hdl)) != 0) { + /* Server must be down */ + serv_p->server_state = SERVER_DOWN; + /* Zero out the ops and entries tables */ + memset(&ctx->ops_tbl, 0x00, sizeof(ctx->ops_tbl)); + memset(&ctx->entries_tbl, 0x00, sizeof(ctx->entries_tbl)); + if (previous_state != SERVER_DOWN) + snmp_log(LOG_INFO, "Unable to open stats file (%s) for server: %d\n", + serv_p->stats_file, serv_p->port); + } else { + /* Initialize ops table */ + if ((err = agt_mread_stats(stats_hdl, &ctx->hdr_tbl, &ctx->ops_tbl, + &ctx->entries_tbl)) != 0) + snmp_log(LOG_ERR, "Unable to read stats file: %s\n", + serv_p->stats_file); + + /* Close stats file */ + if ((err = agt_mclose_stats(stats_hdl)) != 0) + snmp_log(LOG_ERR, "Error closing stats file: %s\n", + serv_p->stats_file); + + /* Server must be down if the stats file hasn't been + * updated in a while */ + if (difftime(time(NULL), ctx->hdr_tbl.updateTime) >= UPDATE_THRESHOLD) { + serv_p->server_state = SERVER_DOWN; + if (previous_state != SERVER_DOWN) + snmp_log(LOG_INFO, "Stats file for server %d hasn't been updated" + " in %d seconds.\n", serv_p->port, UPDATE_THRESHOLD); + } else { + serv_p->server_state = SERVER_UP; + } + } + + /* If the state of the server changed since the last + * load of the stats, send a trap. */ + if (previous_state != STATE_UNKNOWN) { + if (serv_p->server_state != previous_state) { + if (serv_p->server_state == SERVER_UP) { + snmp_log(LOG_INFO, "Detected start of server: %d\n", + serv_p->port); + send_nsDirectoryServerStart_trap(serv_p); + } else { + send_nsDirectoryServerDown_trap(serv_p); + /* Zero out the ops and entries tables */ + memset(&ctx->ops_tbl, 0x00, sizeof(ctx->ops_tbl)); + memset(&ctx->entries_tbl, 0x00, sizeof(ctx->entries_tbl)); + } + } else if (ctx->hdr_tbl.startTime != previous_start) { + /* Send traps if the server has restarted since the last load */ + snmp_log(LOG_INFO, "Detected restart of server: %d\n", serv_p->port); + send_nsDirectoryServerDown_trap(serv_p); + send_nsDirectoryServerStart_trap(serv_p); + } + } + } else { + /* Can't find our row. This shouldn't ever happen. */ + snmp_log(LOG_ERR, "Row not found for server: %d\n", + serv_p->port); + } + } + return 0; +} + +/************************************************************ + * dsOpsTable_get_value + * + * This routine is called for get requests to copy the data + * from the context to the varbind for the request. If the + * context has been properly maintained, you don't need to + * change in code in this fuction. + */ +int +dsOpsTable_get_value(netsnmp_request_info *request, + netsnmp_index * item, + netsnmp_table_request_info *table_info) +{ + netsnmp_variable_list *var = request->requestvb; + stats_table_context *context = (stats_table_context *) item; + + switch (table_info->colnum) { + + case COLUMN_DSANONYMOUSBINDS: + snmp_set_var_typed_value(var, ASN_COUNTER, + (char *) &context->ops_tbl.dsAnonymousBinds, + sizeof(context->ops_tbl.dsAnonymousBinds)); + break; + + case COLUMN_DSUNAUTHBINDS: + snmp_set_var_typed_value(var, ASN_COUNTER, + (char *) &context->ops_tbl.dsUnAuthBinds, + sizeof(context->ops_tbl.dsUnAuthBinds)); + break; + + case COLUMN_DSSIMPLEAUTHBINDS: + snmp_set_var_typed_value(var, ASN_COUNTER, + (char *) &context->ops_tbl.dsSimpleAuthBinds, + sizeof(context->ops_tbl.dsSimpleAuthBinds)); + break; + + case COLUMN_DSSTRONGAUTHBINDS: + snmp_set_var_typed_value(var, ASN_COUNTER, + (char *) &context->ops_tbl.dsStrongAuthBinds, + sizeof(context->ops_tbl.dsStrongAuthBinds)); + break; + + case COLUMN_DSBINDSECURITYERRORS: + snmp_set_var_typed_value(var, ASN_COUNTER, + (char *) &context->ops_tbl.dsBindSecurityErrors, + sizeof(context->ops_tbl.dsBindSecurityErrors)); + break; + + case COLUMN_DSINOPS: + snmp_set_var_typed_value(var, ASN_COUNTER, + (char *) &context->ops_tbl.dsInOps, + sizeof(context->ops_tbl.dsInOps)); + break; + + case COLUMN_DSREADOPS: + snmp_set_var_typed_value(var, ASN_COUNTER, + (char *) &context->ops_tbl.dsReadOps, + sizeof(context->ops_tbl.dsReadOps)); + break; + + case COLUMN_DSCOMPAREOPS: + snmp_set_var_typed_value(var, ASN_COUNTER, + (char *) &context->ops_tbl.dsCompareOps, + sizeof(context->ops_tbl.dsCompareOps)); + break; + + case COLUMN_DSADDENTRYOPS: + snmp_set_var_typed_value(var, ASN_COUNTER, + (char *) &context->ops_tbl.dsAddEntryOps, + sizeof(context->ops_tbl.dsAddEntryOps)); + break; + + case COLUMN_DSREMOVEENTRYOPS: + snmp_set_var_typed_value(var, ASN_COUNTER, + (char *) &context->ops_tbl.dsRemoveEntryOps, + sizeof(context->ops_tbl.dsRemoveEntryOps)); + break; + + case COLUMN_DSMODIFYENTRYOPS: + snmp_set_var_typed_value(var, ASN_COUNTER, + (char *) &context->ops_tbl.dsModifyEntryOps, + sizeof(context->ops_tbl.dsModifyEntryOps)); + break; + + case COLUMN_DSMODIFYRDNOPS: + snmp_set_var_typed_value(var, ASN_COUNTER, + (char *) &context->ops_tbl.dsModifyRDNOps, + sizeof(context->ops_tbl.dsModifyRDNOps)); + break; + + case COLUMN_DSLISTOPS: + snmp_set_var_typed_value(var, ASN_COUNTER, + (char *) &context->ops_tbl.dsListOps, + sizeof(context->ops_tbl.dsListOps)); + break; + + case COLUMN_DSSEARCHOPS: + snmp_set_var_typed_value(var, ASN_COUNTER, + (char *) &context->ops_tbl.dsSearchOps, + sizeof(context->ops_tbl.dsSearchOps)); + break; + + case COLUMN_DSONELEVELSEARCHOPS: + snmp_set_var_typed_value(var, ASN_COUNTER, + (char *) &context->ops_tbl.dsOneLevelSearchOps, + sizeof(context->ops_tbl.dsOneLevelSearchOps)); + break; + + case COLUMN_DSWHOLESUBTREESEARCHOPS: + snmp_set_var_typed_value(var, ASN_COUNTER, + (char *) &context->ops_tbl.dsWholeSubtreeSearchOps, + sizeof(context->ops_tbl.dsWholeSubtreeSearchOps)); + break; + + case COLUMN_DSREFERRALS: + snmp_set_var_typed_value(var, ASN_COUNTER, + (char *) &context->ops_tbl.dsReferrals, + sizeof(context->ops_tbl.dsReferrals)); + break; + + case COLUMN_DSCHAININGS: + snmp_set_var_typed_value(var, ASN_COUNTER, + (char *) &context->ops_tbl.dsChainings, + sizeof(context->ops_tbl.dsChainings)); + break; + + case COLUMN_DSSECURITYERRORS: + snmp_set_var_typed_value(var, ASN_COUNTER, + (char *) &context->ops_tbl.dsSecurityErrors, + sizeof(context->ops_tbl.dsSecurityErrors)); + break; + + case COLUMN_DSERRORS: + snmp_set_var_typed_value(var, ASN_COUNTER, + (char *) &context->ops_tbl.dsErrors, + sizeof(context->ops_tbl.dsErrors)); + break; + + default:/* We shouldn't get here */ + snmp_log(LOG_ERR, "Unknown column in dsOpsTable_get_value\n"); + return SNMP_ERR_GENERR; + } + return SNMP_ERR_NOERROR; +} + +/************************************************************ + * dsEntriesTable_get_value + * + * This routine is called for get requests to copy the data + * from the context to the varbind for the request. If the + * context has been properly maintained, you don't need to + * change in code in this fuction. + */ +int +dsEntriesTable_get_value(netsnmp_request_info *request, + netsnmp_index * item, + netsnmp_table_request_info *table_info) +{ + netsnmp_variable_list *var = request->requestvb; + stats_table_context *context = (stats_table_context *) item; + + switch (table_info->colnum) { + + case COLUMN_DSMASTERENTRIES: + snmp_set_var_typed_value(var, ASN_GAUGE, + (char *) &context->entries_tbl.dsMasterEntries, + sizeof(context->entries_tbl.dsMasterEntries)); + break; + + case COLUMN_DSCOPYENTRIES: + snmp_set_var_typed_value(var, ASN_GAUGE, + (char *) &context->entries_tbl.dsCopyEntries, + sizeof(context->entries_tbl.dsCopyEntries)); + break; + + case COLUMN_DSCACHEENTRIES: + snmp_set_var_typed_value(var, ASN_GAUGE, + (char *) &context->entries_tbl.dsCacheEntries, + sizeof(context->entries_tbl.dsCacheEntries)); + break; + + case COLUMN_DSCACHEHITS: + snmp_set_var_typed_value(var, ASN_COUNTER, + (char *) &context->entries_tbl.dsCacheHits, + sizeof(context->entries_tbl.dsCacheHits)); + break; + + case COLUMN_DSSLAVEHITS: + snmp_set_var_typed_value(var, ASN_COUNTER, + (char *) &context->entries_tbl.dsSlaveHits, + sizeof(context->entries_tbl.dsSlaveHits)); + break; + + default:/* We shouldn't get here */ + snmp_log(LOG_ERR, "Unknown column in dsEntriesTable_get_value\n"); + return SNMP_ERR_GENERR; + } + return SNMP_ERR_NOERROR; +} + +/************************************************************ + * dsEntityTable_get_value + * + * This routine is called for get requests to copy the data + * from the context to the varbind for the request. If the + * context has been properly maintained, you don't need to + * change in code in this fuction. + */ +int +dsEntityTable_get_value(netsnmp_request_info *request, + netsnmp_index * item, + netsnmp_table_request_info *table_info) +{ + netsnmp_variable_list *var = request->requestvb; + stats_table_context *context = (stats_table_context *) item; + server_instance *server = (server_instance *) context->entity_tbl; + + switch (table_info->colnum) { + + case COLUMN_DSENTITYDESCR: + snmp_set_var_typed_value(var, ASN_OCTET_STR, + (char *) server->description, + strlen(server->description)); + break; + + case COLUMN_DSENTITYVERS: + snmp_set_var_typed_value(var, ASN_OCTET_STR, + (char *) context->hdr_tbl.dsVersion, + strlen(context->hdr_tbl.dsVersion)); + break; + + case COLUMN_DSENTITYORG: + snmp_set_var_typed_value(var, ASN_OCTET_STR, + (char *) server->org, + strlen(server->org)); + break; + + case COLUMN_DSENTITYLOCATION: + snmp_set_var_typed_value(var, ASN_OCTET_STR, + (char *) server->location, + strlen(server->location)); + break; + + case COLUMN_DSENTITYCONTACT: + snmp_set_var_typed_value(var, ASN_OCTET_STR, + (char *) server->contact, + strlen(server->contact)); + break; + + case COLUMN_DSENTITYNAME: + snmp_set_var_typed_value(var, ASN_OCTET_STR, + (char *) server->name, + strlen(server->name)); + break; + + default:/* We shouldn't get here */ + snmp_log(LOG_ERR, "Unknown column in dsEntityTable_get_value\n"); + return SNMP_ERR_GENERR; + } + return SNMP_ERR_NOERROR; +} + +/************************************************************ + * send_nsDirectoryServerDown_trap + * + * Sends off the server down trap. + */ +int +send_nsDirectoryServerDown_trap(server_instance *serv_p) +{ + netsnmp_variable_list *var_list = NULL; + + snmp_log(LOG_INFO, "Sending down trap for server: %d\n", serv_p->port); + + /* Define the oids for the trap */ + oid nsDirectoryServerDown_oid[] = { nsDirectoryServerDown_OID }; + oid dsEntityDescr_oid[] = { dsEntityTable_TABLE_OID, 1, COLUMN_DSENTITYDESCR, serv_p->port }; + oid dsEntityVers_oid[] = { dsEntityTable_TABLE_OID, 1, COLUMN_DSENTITYVERS, serv_p->port }; + oid dsEntityLocation_oid[] = { dsEntityTable_TABLE_OID, 1, COLUMN_DSENTITYLOCATION, serv_p->port }; + oid dsEntityContact_oid[] = { dsEntityTable_TABLE_OID, 1, COLUMN_DSENTITYCONTACT, serv_p->port }; + + /* Setup the variable list to send with the trap */ + snmp_varlist_add_variable(&var_list, + snmptrap_oid, OID_LENGTH(snmptrap_oid), + ASN_OBJECT_ID, + (u_char *) &nsDirectoryServerDown_oid, + sizeof(nsDirectoryServerDown_oid)); + snmp_varlist_add_variable(&var_list, + dsEntityDescr_oid, + OID_LENGTH(dsEntityDescr_oid), ASN_OCTET_STR, + (char *) serv_p->description, + strlen(serv_p->description)); + snmp_varlist_add_variable(&var_list, + dsEntityVers_oid, + OID_LENGTH(dsEntityVers_oid), ASN_OCTET_STR, + (char *) serv_p->version, + strlen(serv_p->version)); + snmp_varlist_add_variable(&var_list, + dsEntityLocation_oid, + OID_LENGTH(dsEntityLocation_oid), + ASN_OCTET_STR, + (char *) serv_p->location, + strlen(serv_p->location)); + snmp_varlist_add_variable(&var_list, + dsEntityContact_oid, + OID_LENGTH(dsEntityContact_oid), + ASN_OCTET_STR, + (char *) serv_p->contact, + strlen(serv_p->contact)); + + /* Send the trap */ + send_v2trap(var_list); + snmp_free_varbind(var_list); + + return SNMP_ERR_NOERROR; +} + +/************************************************************ + * send_nsDirectoryServerStart_trap + * + * Sends off the server start trap. + */ +int +send_nsDirectoryServerStart_trap(server_instance *serv_p) +{ + netsnmp_variable_list *var_list = NULL; + + snmp_log(LOG_INFO, "Sending start trap for server: %d\n", serv_p->port); + + /* Define the oids for the trap */ + oid nsDirectoryServerStart_oid[] = { nsDirectoryServerStart_OID }; + oid dsEntityDescr_oid[] = { dsEntityTable_TABLE_OID, 1, COLUMN_DSENTITYDESCR, serv_p->port }; + oid dsEntityVers_oid[] = { dsEntityTable_TABLE_OID, 1, COLUMN_DSENTITYVERS, serv_p->port }; + oid dsEntityLocation_oid[] = { dsEntityTable_TABLE_OID, 1, COLUMN_DSENTITYLOCATION, serv_p->port }; + + /* Setup the variable list to send with the trap */ + snmp_varlist_add_variable(&var_list, + snmptrap_oid, OID_LENGTH(snmptrap_oid), + ASN_OBJECT_ID, + (u_char *) &nsDirectoryServerStart_oid, + sizeof(nsDirectoryServerStart_oid)); + snmp_varlist_add_variable(&var_list, + dsEntityDescr_oid, + OID_LENGTH(dsEntityDescr_oid), ASN_OCTET_STR, + (char *) serv_p->description, + strlen(serv_p->description)); + snmp_varlist_add_variable(&var_list, + dsEntityVers_oid, + OID_LENGTH(dsEntityVers_oid), ASN_OCTET_STR, + (char *) serv_p->version, + strlen(serv_p->version)); + snmp_varlist_add_variable(&var_list, + dsEntityLocation_oid, + OID_LENGTH(dsEntityLocation_oid), + ASN_OCTET_STR, + (char *) serv_p->location, + strlen(serv_p->location)); + + /* Send the trap */ + send_v2trap(var_list); + snmp_free_varbind(var_list); + + return SNMP_ERR_NOERROR; +} diff --git a/ldap/servers/snmp/ldap-agent.h b/ldap/servers/snmp/ldap-agent.h new file mode 100644 index 00000000..6acf79c6 --- /dev/null +++ b/ldap/servers/snmp/ldap-agent.h @@ -0,0 +1,146 @@ +#ifndef DSOPSTABLE_H +#define DSOPSTABLE_H + +#ifdef __cplusplus +extern "C" { +#endif + + +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/library/container.h> +#include <net-snmp/agent/table_array.h> +#include "agtmmap.h" + +#define MAXLINE 4096 +#define CACHE_REFRESH_INTERVAL 15 +#define UPDATE_THRESHOLD 20 +#define LDAP_AGENT_PIDFILE ".ldap-agent.pid" +#define LDAP_AGENT_LOGFILE "ldap-agent.log" + +/************************************************************* + * Trap value defines + */ +#define SERVER_UP 7002 +#define SERVER_DOWN 7001 +#define STATE_UNKNOWN 0 + +/************************************************************* + * Structures + */ +typedef struct server_instance_s { + PRUint32 port; + int server_state; + char *stats_file; + char *dse_ldif; + char *description; + char *version; + char *org; + char *location; + char *contact; + char *name; + struct server_instance_s *next; +} server_instance; + +typedef struct stats_table_context_s { + netsnmp_index index; + struct hdr_stats_t hdr_tbl; + struct ops_stats_t ops_tbl; + struct entries_stats_t entries_tbl; + server_instance *entity_tbl; +} stats_table_context; + +/************************************************************* + * Function Declarations + */ + void exit_usage(); + void load_config(char *); + void init_ldap_agent(void); + void initialize_stats_table(void); + int load_stats_table(netsnmp_cache *, void *); + stats_table_context *stats_table_create_row(unsigned long); + stats_table_context *stats_table_find_row(unsigned long); + int dsOpsTable_get_value(netsnmp_request_info *, + netsnmp_index *, + netsnmp_table_request_info *); + int dsEntriesTable_get_value(netsnmp_request_info *, + netsnmp_index *, + netsnmp_table_request_info *); + int dsEntityTable_get_value(netsnmp_request_info *, + netsnmp_index *, + netsnmp_table_request_info *); + int send_nsDirectoryServerDown_trap(server_instance *); + int send_nsDirectoryServerStart_trap(server_instance *); + +/************************************************************* + * Oid Declarations + */ + extern oid dsOpsTable_oid[]; + extern size_t dsOpsTable_oid_len; + extern oid dsEntriesTable_oid[]; + extern size_t dsEntriesTable_oid_len; + extern oid dsEntityTable_oid[]; + extern size_t dsEntityTable_oid_len; + extern oid snmptrap_oid[]; + extern size_t snmptrap_oid_len; + +#define enterprise_OID 1,3,6,1,4,1,1450 +#define dsOpsTable_TABLE_OID enterprise_OID,7,1 +#define dsEntriesTable_TABLE_OID enterprise_OID,7,2 +#define dsEntityTable_TABLE_OID enterprise_OID,7,5 +#define snmptrap_OID 1,3,6,1,6,3,1,1,4,1,0 +#define nsDirectoryServerDown_OID enterprise_OID,0,7001 +#define nsDirectoryServerStart_OID enterprise_OID,0,7002 + +/************************************************************* + * dsOpsTable column defines + */ +#define COLUMN_DSANONYMOUSBINDS 1 +#define COLUMN_DSUNAUTHBINDS 2 +#define COLUMN_DSSIMPLEAUTHBINDS 3 +#define COLUMN_DSSTRONGAUTHBINDS 4 +#define COLUMN_DSBINDSECURITYERRORS 5 +#define COLUMN_DSINOPS 6 +#define COLUMN_DSREADOPS 7 +#define COLUMN_DSCOMPAREOPS 8 +#define COLUMN_DSADDENTRYOPS 9 +#define COLUMN_DSREMOVEENTRYOPS 10 +#define COLUMN_DSMODIFYENTRYOPS 11 +#define COLUMN_DSMODIFYRDNOPS 12 +#define COLUMN_DSLISTOPS 13 +#define COLUMN_DSSEARCHOPS 14 +#define COLUMN_DSONELEVELSEARCHOPS 15 +#define COLUMN_DSWHOLESUBTREESEARCHOPS 16 +#define COLUMN_DSREFERRALS 17 +#define COLUMN_DSCHAININGS 18 +#define COLUMN_DSSECURITYERRORS 19 +#define COLUMN_DSERRORS 20 +#define dsOpsTable_COL_MIN 1 +#define dsOpsTable_COL_MAX 20 + +/************************************************************* + * dsEntriesTable column defines + */ +#define COLUMN_DSMASTERENTRIES 1 +#define COLUMN_DSCOPYENTRIES 2 +#define COLUMN_DSCACHEENTRIES 3 +#define COLUMN_DSCACHEHITS 4 +#define COLUMN_DSSLAVEHITS 5 +#define dsEntriesTable_COL_MIN 1 +#define dsEntriesTable_COL_MAX 5 + +/************************************************************* + * dsEntityTable column defines + */ +#define COLUMN_DSENTITYDESCR 1 +#define COLUMN_DSENTITYVERS 2 +#define COLUMN_DSENTITYORG 3 +#define COLUMN_DSENTITYLOCATION 4 +#define COLUMN_DSENTITYCONTACT 5 +#define COLUMN_DSENTITYNAME 6 +#define dsEntityTable_COL_MIN 1 +#define dsEntityTable_COL_MAX 6 + +#ifdef __cplusplus +} +#endif +#endif /** DSOPSTABLE_H */ diff --git a/ldap/servers/snmp/main.c b/ldap/servers/snmp/main.c new file mode 100644 index 00000000..a0900de3 --- /dev/null +++ b/ldap/servers/snmp/main.c @@ -0,0 +1,367 @@ +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> +#include <signal.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <signal.h> +#include <sys/stat.h> +#include <ldap-agent.h> + +static char *agentx_master = NULL; +static char *agent_logdir = NULL; +static char *pidfile = NULL; +server_instance *server_head = NULL; + +static int keep_running; + +RETSIGTYPE +stop_server(int signum) { + if (signum == SIGUSR1) { + snmp_log(LOG_INFO, "Detected attempt to start ldap-agent again.\n"); + } else { + snmp_log(LOG_INFO, "Received stop signal. Stopping ldap-agent...\n"); + keep_running = 0; + } +} + +int +main (int argc, char *argv[]) { + char *config_file = NULL; + netsnmp_log_handler *log_hdl = NULL; + int c, log_level = LOG_INFO; + struct stat logdir_s; + pid_t child_pid; + FILE *pid_fp; + + /* Load options */ + while ((--argc > 0) && ((*++argv)[0] == '-')) { + while (c = *++argv[0]) { + switch (c) { + case 'D': + log_level = LOG_DEBUG; + break; + default: + printf("ldap-agent: illegal option %c\n", c); + exit_usage(); + } + } + } + + if (argc != 1) + exit_usage(); + + /* load config file */ + if ((config_file = strdup(*argv)) == NULL) { + printf("ldap-agent: Memory error loading config file\n"); + exit(1); + } + + load_config(config_file); + + /* check if we're already running as another process */ + if ((pid_fp = fopen(pidfile, "r")) != NULL) { + fscanf(pid_fp, "%d", &child_pid); + fclose(pid_fp); + if (kill(child_pid, SIGUSR1) == 0) { + printf("ldap-agent: Already running as pid %d!\n", child_pid); + exit(1); + } else { + /* old pidfile exists, but the process doesn't. Cleanup pidfile */ + remove(pidfile); + } + } + + /* start logging */ + netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_LOG_TIMESTAMP, 1); + + if ((log_hdl = netsnmp_register_loghandler(NETSNMP_LOGHANDLER_FILE, + log_level)) != NULL) { + if (agent_logdir != NULL) { + /* Verify agent-logdir setting */ + if (stat(agent_logdir, &logdir_s) < 0) { + printf("ldap-agent: Error reading logdir: %s\n", agent_logdir); + exit(1); + } else { + /* Is it a directory? */ + if (S_ISDIR(logdir_s.st_mode)) { + /* Can we write to it? */ + if (access(agent_logdir, W_OK) < 0) { + printf("ldap-agent: Unable to write to logdir: %s\n", + agent_logdir); + exit(1); + } + } else { + printf("ldap-agent: agent-logdir setting must point to a directory.\n"); + exit(1); + } + } + + /* agent-logdir setting looks ok */ + if ((log_hdl->token = malloc(strlen(agent_logdir) + + strlen(LDAP_AGENT_LOGFILE) + 2)) != NULL) { + strncpy((char *) log_hdl->token, agent_logdir, strlen(agent_logdir) + 1); + /* add a trailing slash if needed */ + if (*(agent_logdir + strlen(agent_logdir)) != '/') + strcat((char *) log_hdl->token, "/"); + strcat((char *) log_hdl->token, LDAP_AGENT_LOGFILE); + } + } else { + /* agent-logdir not set, so write locally */ + log_hdl->token = strdup(LDAP_AGENT_LOGFILE); + } + + netsnmp_enable_filelog(log_hdl, 1); + } else { + printf("Error starting logging."); + exit(1); + } + + snmp_log(LOG_INFO, "Starting ldap-agent...\n"); + + /* setup agentx master */ + netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, + NETSNMP_DS_AGENT_ROLE, 1); + if (agentx_master) + netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, + NETSNMP_DS_AGENT_X_SOCKET, agentx_master); + + /* run as a daemon */ + if (netsnmp_daemonize(0, 0)) { + /* sleep to allow pidfile to be created by child */ + sleep(3); + if((pid_fp = fopen(pidfile,"r")) == NULL) { + printf("ldap-agent: Not started! Check log file for details.\n"); + exit(1); + } else { + fscanf(pid_fp, "%d", &child_pid); + fclose(pid_fp); + } + printf("ldap-agent: Started as pid %d\n", child_pid); + exit(1); + } + + /* initialize the agent */ + init_agent("ldap-agent"); + init_ldap_agent(); + init_snmp("ldap-agent"); + + /* listen for signals */ + keep_running = 1; + signal(SIGUSR1, stop_server); + signal(SIGTERM, stop_server); + signal(SIGINT, stop_server); + + /* create pidfile in config file dir */ + child_pid = getpid(); + if ((pid_fp = fopen(pidfile, "w")) == NULL) { + snmp_log(LOG_ERR, "Error creating pid file: %s\n", pidfile); + exit(1); + } else { + if (fprintf(pid_fp, "%d", child_pid) < 0) { + snmp_log(LOG_ERR, "Error writing pid file: %s\n", pidfile); + exit(1); + } + fclose(pid_fp); + } + + /* we're up and running! */ + snmp_log(LOG_INFO, "Started ldap-agent as pid %d\n", child_pid); + + /* loop here until asked to stop */ + while(keep_running) { + agent_check_and_process(1); + } + + /* say goodbye */ + snmp_shutdown("ldap-agent"); + snmp_log(LOG_INFO, "ldap-agent stopped.\n"); + + /* remove pidfile */ + remove(pidfile); + + return 0; +} + +/************************************************************************ + * load_config + * + * Loads subagent config file and reads directory server config files. + */ +void +load_config(char *conf_path) +{ + server_instance *serv_p = NULL; + FILE *conf_file = NULL; + FILE *dse_fp = NULL; + char line[MAXLINE]; + char *p = NULL; + char *p2 = NULL; + + /* Open config file */ + if ((conf_file = fopen(conf_path, "r")) == NULL) { + printf("ldap-agent: Error opening config file: %s\n", conf_path); + exit(1); + } + + /* set pidfile path */ + for (p = (conf_path + strlen(conf_path) - 1); p >= conf_path; p--) { + if (*p == '/') { + if ((pidfile = malloc((p - conf_path) + + strlen(LDAP_AGENT_PIDFILE) + 2)) != NULL) { + strncpy(pidfile, conf_path, (p - conf_path + 1)); + strcat(pidfile, LDAP_AGENT_PIDFILE); + break; + } else { + printf("ldap-agent: malloc error processing config file\n"); + exit(1); + } + } + } + + while (fgets(line, MAXLINE, conf_file) != NULL) { + /* Ignore comment lines in config file */ + if (line[0] == '#') + continue; + + if ((p = strstr(line, "agentx-master")) != NULL) { + /* load agentx-master setting */ + p = p + 13; + if ((p = strtok(p, " \t\n")) != NULL) { + if ((agentx_master = (char *) malloc(strlen(p) + 1)) != NULL) + strcpy(agentx_master, p); + } + } else if ((p = strstr(line, "agent-logdir")) != NULL) { + /* load agent-logdir setting */ + p = p + 12; + if ((p = strtok(p, " \t\n")) != NULL) { + if ((agent_logdir = (char *) malloc(strlen(p) + 1)) != NULL) + strcpy(agent_logdir, p); + } + } else if ((p = strstr(line, "server")) != NULL) { + /* Allocate a server_instance */ + if ((serv_p = malloc(sizeof(server_instance))) == NULL) { + printf("ldap-agent: malloc error processing config file\n"); + exit(1); + } + + /* load server setting */ + p = p + 6; + if ((p = strtok_r(p, " :\t\n", &p2)) != NULL) { + /* first token is the instance root */ + if ((serv_p->stats_file = malloc(strlen(p) + 18)) != NULL) + snprintf(serv_p->stats_file, strlen(p) + 18, + "%s/logs/slapd.stats", p); + if ((serv_p->dse_ldif = malloc(strlen(p) + 17)) != NULL) { + snprintf(serv_p->dse_ldif, strlen(p) + 17, "%s/config/dse.ldif", p); + } + + /* second token is the name */ + p = p2; + if((p2 = strchr(p, ':')) != NULL) { + *p2 = '\0'; + ++p2; + if ((serv_p->name = malloc(strlen(p) + 1)) != NULL) + snprintf(serv_p->name, strlen(p) + 1, "%s", p); + } else { + printf("ldap-agent: Invalid config file\n"); + exit(1); + } + + /* third token is the description */ + p = p2; + if((p2 = strchr(p, ':')) != NULL) { + *p2 = '\0'; + ++p2; + if ((serv_p->description = malloc(strlen(p) + 1)) != NULL) + snprintf(serv_p->description, strlen(p) + 1, "%s", p); + } else { + printf("ldap-agent: Invalid config file\n"); + exit(1); + } + + /* fourth token is the org */ + p = p2; + if((p2 = strchr(p, ':')) != NULL) { + *p2 = '\0'; + ++p2; + if ((serv_p->org = malloc(strlen(p) + 1)) != NULL) + snprintf(serv_p->org, strlen(p) + 1, "%s", p); + } else { + printf("ldap-agent: Invalid config file\n"); + exit(1); + } + + /* fifth token is the location */ + p = p2; + if((p2 = strchr(p, ':')) != NULL) { + *p2 = '\0'; + ++p2; + if ((serv_p->location = malloc(strlen(p) + 1)) != NULL) + snprintf(serv_p->location, strlen(p) + 1, "%s", p); + } else { + printf("ldap-agent: Invalid config file\n"); + exit(1); + } + + /* sixth token is the contact */ + p = p2; + if((p2 = strchr(p, '\n')) != NULL) { + *p2 = '\0'; + if ((serv_p->contact = malloc(strlen(p) + 1)) != NULL) + snprintf(serv_p->contact, strlen(p) + 1, "%s", p); + } else { + printf("ldap-agent: Invalid config file\n"); + exit(1); + } + } + + /* Open dse.ldif */ + if ((dse_fp = fopen(serv_p->dse_ldif, "r")) == NULL) { + printf("ldap-agent: Error opening server config file: %s\n", + serv_p->dse_ldif); + exit(1); + } + + /* Get port value */ + while (fgets(line, MAXLINE, dse_fp) != NULL) { + if ((p = strstr(line, "nsslapd-port: ")) != NULL) { + p = p + 14; + if ((p = strtok(p, ": \t\n")) != NULL) + serv_p->port = atol(p); + } + } + + /* Close dse.ldif */ + fclose(dse_fp); + + /* Insert server instance into linked list */ + serv_p->next = server_head; + server_head = serv_p; + } + } + + /* Close config file */ + fclose(conf_file); + + /* check for at least one directory server instance */ + if (server_head == NULL) { + printf("ldap-agent: No server instances defined in config file\n"); + exit(1); + } +} + +/************************************************************************ + * exit_usage + * + * Prints usage message and exits program. + */ +void +exit_usage() +{ + printf("Usage: ldap-agent [-D] configfile\n"); + printf(" -D Enable debug logging\n"); + exit(1); +} |