/* * Dynamic Loadable Agent Modules MIB (UCD-DLMOD-MIB) - dlmod.c * */ #include #if HAVE_STDLIB_H #include #endif #include #if HAVE_STRING_H #include #else #include #endif #if HAVE_UNISTD_H #include #endif #include #if HAVE_WINSOCK_H #include #endif #include #include #include "struct.h" #include "util_funcs.h" #if defined(HAVE_DLFCN_H) && defined(HAVE_DLOPEN) #include #include "dlmod.h" static struct dlmod *dlmods = NULL; static int dlmod_next_index = 1; static char dlmod_path[1024]; static void dlmod_parse_config(const char *, char *); static void dlmod_free_config(void); /* * this variable defines function callbacks and type return * information for the dlmod mib */ static struct variable4 dlmod_variables[] = { {DLMODNEXTINDEX, ASN_INTEGER, RONLY, var_dlmod, 1, {1}}, {DLMODNAME, ASN_OCTET_STR, RWRITE, var_dlmodEntry, 3, {2, 1, 2}}, {DLMODPATH, ASN_OCTET_STR, RWRITE, var_dlmodEntry, 3, {2, 1, 3}}, {DLMODERROR, ASN_OCTET_STR, RONLY, var_dlmodEntry, 3, {2, 1, 4}}, {DLMODSTATUS, ASN_INTEGER, RWRITE, var_dlmodEntry, 3, {2, 1, 5}}, }; static oid dlmod_variables_oid[] = { 1, 3, 6, 1, 4, 1, 2021, 13, 14 }; static int dlmod_variables_oid_len = 9; void init_dlmod(void) { char *p; int len; REGISTER_MIB("dlmod", dlmod_variables, variable4, dlmod_variables_oid); /* * TODO: REGISTER_SYSOR_ENTRY */ DEBUGMSGTL(("dlmod", "register mib\n")); snmpd_register_config_handler("dlmod", dlmod_parse_config, dlmod_free_config, "module-name module-path"); p = getenv("SNMPDLMODPATH"); strncpy(dlmod_path, SNMPDLMODPATH, sizeof(dlmod_path)); dlmod_path[ sizeof(dlmod_path)-1 ] = 0; if (p) { if (p[0] == ':') { len = strlen(dlmod_path); if (dlmod_path[len - 1] != ':') { strncat(dlmod_path, ":", sizeof(dlmod_path) - len -1); len++; } strncat(dlmod_path, p + 1, sizeof(dlmod_path) - len); } else strncpy(dlmod_path, p, sizeof(dlmod_path)); } dlmod_path[ sizeof(dlmod_path)-1 ] = 0; DEBUGMSGTL(("dlmod", "dlmod_path: %s\n", dlmod_path)); } void deinit_dlmod(void) { unregister_mib(dlmod_variables_oid, dlmod_variables_oid_len); snmpd_unregister_config_handler("dlmod"); } struct dlmod * dlmod_create_module(void) { struct dlmod **pdlmod, *dlm; DEBUGMSGTL(("dlmod", "dlmod_create_module\n")); dlm = (struct dlmod *) calloc(1, sizeof(struct dlmod)); if (dlm == NULL) return NULL; dlm->index = dlmod_next_index++; dlm->status = DLMOD_UNLOADED; for (pdlmod = &dlmods; *pdlmod != NULL; pdlmod = &((*pdlmod)->next)); (*pdlmod) = dlm; return dlm; } void dlmod_delete_module(struct dlmod *dlm) { struct dlmod **pdlmod; DEBUGMSGTL(("dlmod", "dlmod_delete_module\n")); if (!dlm || dlm->status != DLMOD_UNLOADED) return; for (pdlmod = &dlmods; *pdlmod; pdlmod = &((*pdlmod)->next)) if (*pdlmod == dlm) { *pdlmod = dlm->next; free(dlm); return; } } void dlmod_load_module(struct dlmod *dlm) { char sym_init[64]; char *p, tmp_path[255]; int (*dl_init) (void); DEBUGMSGTL(("dlmod", "dlmod_load_module %s: %s\n", dlm->name, dlm->path)); if (!dlm || !dlm->path || !dlm->name || (dlm->status != DLMOD_UNLOADED && dlm->status != DLMOD_ERROR)) return; if (dlm->path[0] == '/') { #ifdef RTLD_NOW dlm->handle = dlopen(dlm->path, RTLD_NOW); #else dlm->handle = dlopen(dlm->path, RTLD_LAZY); #endif if (dlm->handle == NULL) { snprintf(dlm->error, sizeof(dlm->error), "dlopen failed: %s", dlerror()); dlm->status = DLMOD_ERROR; return; } } else { for (p = strtok(dlmod_path, ":"); p; p = strtok(NULL, ":")) { snprintf(tmp_path, sizeof(tmp_path), "%s/%s.so", p, dlm->path); DEBUGMSGTL(("dlmod", "p: %s tmp_path: %s\n", p, tmp_path)); #ifdef RTLD_NOW dlm->handle = dlopen(tmp_path, RTLD_NOW); #else dlm->handle = dlopen(tmp_path, RTLD_LAZY); #endif if (dlm->handle == NULL) { snprintf(dlm->error, sizeof(dlm->error), "dlopen failed: %s", dlerror()); dlm->status = DLMOD_ERROR; } } strncpy(dlm->path, tmp_path, sizeof(dlm->path)); if (dlm->status == DLMOD_ERROR) return; } snprintf(sym_init, sizeof(sym_init), "init_%s", dlm->name); dl_init = dlsym(dlm->handle, sym_init); if (dl_init == NULL) { dlclose(dlm->handle); snprintf(dlm->error, sizeof(dlm->error), "dlsym failed: can't find \'%s\'", sym_init); dlm->status = DLMOD_ERROR; return; } dl_init(); dlm->error[0] = '\0'; dlm->status = DLMOD_LOADED; } void dlmod_unload_module(struct dlmod *dlm) { char sym_deinit[64]; int (*dl_deinit) (void); if (!dlm || dlm->status != DLMOD_LOADED) return; snprintf(sym_deinit, sizeof(sym_deinit), "deinit_%s", dlm->name); dl_deinit = dlsym(dlm->handle, sym_deinit); if (dl_deinit == NULL) { snprintf(dlm->error, sizeof(dlm->error), "dlsym failed: can't find \'%s\'", sym_deinit); } else { dl_deinit(); } dlclose(dlm->handle); dlm->status = DLMOD_UNLOADED; DEBUGMSGTL(("dlmod", "Module %s unloaded\n", dlm->name)); } struct dlmod * dlmod_get_by_index(int iindex) { struct dlmod *dlmod; for (dlmod = dlmods; dlmod; dlmod = dlmod->next) if (dlmod->index == iindex) return dlmod; return NULL; } static void dlmod_parse_config(const char *token, char *cptr) { char *dlm_name, *dlm_path; struct dlmod *dlm; if (cptr == NULL) { config_perror("Bad dlmod line"); return; } /* * remove comments */ *(cptr + strcspn(cptr, "#;\r\n")) = '\0'; dlm = dlmod_create_module(); if (!dlm) return; /* * dynamic module name */ dlm_name = strtok(cptr, "\t "); if (dlm_name == NULL) { config_perror("Bad dlmod line"); dlmod_delete_module(dlm); return; } strncpy(dlm->name, dlm_name, sizeof(dlm->name)); /* * dynamic module path */ dlm_path = strtok(NULL, "\t "); if (dlm_path) strncpy(dlm->path, dlm_path, sizeof(dlm->path)); else strncpy(dlm->path, dlm_name, sizeof(dlm->path)); dlmod_load_module(dlm); if (dlm->status == DLMOD_ERROR) snmp_log(LOG_ERR, "%s\n", dlm->error); } static void dlmod_free_config(void) { struct dlmod *dtmp, *dtmp2; for (dtmp = dlmods; dtmp != NULL;) { dtmp2 = dtmp; dtmp = dtmp->next; dlmod_unload_module(dtmp2); free(dtmp2); } dlmods = NULL; } /* * header_dlmod(... * Arguments: * vp IN - pointer to variable entry that points here * name IN/OUT - IN/name requested, OUT/name found * length IN/OUT - length of IN/OUT oid's * exact IN - TRUE if an exact match was requested * var_len OUT - length of variable or 0 if function returned * write_method */ static int header_dlmod(struct variable *vp, oid * name, size_t * length, int exact, size_t * var_len, WriteMethod ** write_method) { #define DLMOD_NAME_LENGTH 10 oid newname[MAX_OID_LEN]; int result; memcpy((char *) newname, (char *) vp->name, (int) vp->namelen * sizeof(oid)); newname[DLMOD_NAME_LENGTH] = 0; result = snmp_oid_compare(name, *length, newname, (int) vp->namelen + 1); if ((exact && (result != 0)) || (!exact && (result >= 0))) { return MATCH_FAILED; } memcpy((char *) name, (char *) newname, ((int) vp->namelen + 1) * sizeof(oid)); *length = vp->namelen + 1; *write_method = 0; *var_len = sizeof(long); /* default to 'long' results */ return MATCH_SUCCEEDED; } u_char * var_dlmod(struct variable * vp, oid * name, size_t * length, int exact, size_t * var_len, WriteMethod ** write_method) { /* * variables we may use later */ *write_method = 0; /* assume it isnt writable for the time being */ *var_len = sizeof(int); /* assume an integer and change later * if not */ if (header_dlmod(vp, name, length, exact, var_len, write_method) == MATCH_FAILED) return 0; /* * this is where we do the value assignments for the mib results. */ switch (vp->magic) { case DLMODNEXTINDEX: long_return = dlmod_next_index; return (unsigned char *) &long_return; default: DEBUGMSGTL(("dlmod", "unknown sub-id %d in var_dlmod\n", vp->magic)); } return 0; } /* * header_dlmodEntry(... * Arguments: * vp IN - pointer to variable entry that points here * name IN/OUT - IN/name requested, OUT/name found * length IN/OUT - length of IN/OUT oid's * exact IN - TRUE if an exact match was requested * var_len OUT - length of variable or 0 if function returned * write_method * */ static struct dlmod * header_dlmodEntry(struct variable *vp, oid * name, size_t * length, int exact, size_t * var_len, WriteMethod ** write_method) { #define DLMODENTRY_NAME_LENGTH 12 oid newname[MAX_OID_LEN]; int result; struct dlmod *dlm = 0; int dlmod_index; memcpy((char *) newname, (char *) vp->name, (int) vp->namelen * sizeof(oid)); *write_method = 0; for (dlmod_index = 1; dlmod_index < dlmod_next_index; dlmod_index++) { dlm = dlmod_get_by_index(dlmod_index); DEBUGMSGTL(("dlmod", "dlmodEntry dlm: %x dlmod_index: %d\n", (int) dlm, dlmod_index)); if (dlm) { newname[12] = dlmod_index; result = snmp_oid_compare(name, *length, newname, (int) vp->namelen + 1); if ((exact && (result == 0)) || (!exact && (result < 0))) break; } } if (dlmod_index >= dlmod_next_index) { if (dlmod_index == dlmod_next_index && exact && vp->magic == DLMODSTATUS) *write_method = write_dlmodStatus; return NULL; } memcpy((char *) name, (char *) newname, ((int) vp->namelen + 1) * sizeof(oid)); *length = vp->namelen + 1; *var_len = sizeof(long); return dlm; } u_char * var_dlmodEntry(struct variable * vp, oid * name, size_t * length, int exact, size_t * var_len, WriteMethod ** write_method) { /* * variables we may use later */ struct dlmod *dlm; *var_len = sizeof(int); /* assume an integer and change later * if not */ dlm = header_dlmodEntry(vp, name, length, exact, var_len, write_method); if (dlm == NULL) return 0; /* * this is where we do the value assignments for the mib results. */ switch (vp->magic) { case DLMODNAME: *write_method = write_dlmodName; *var_len = strlen(dlm->name); return (unsigned char *) dlm->name; case DLMODPATH: *write_method = write_dlmodPath; *var_len = strlen(dlm->path); return (unsigned char *) dlm->path; case DLMODERROR: *var_len = strlen(dlm->error); return (unsigned char *) dlm->error; case DLMODSTATUS: *write_method = write_dlmodStatus; long_return = dlm->status; return (unsigned char *) &long_return; default: DEBUGMSGTL(("dlmod", "unknown sub-id %d in var_dlmodEntry\n", vp->magic)); } return 0; } int write_dlmodName(int action, u_char * var_val, u_char var_val_type, size_t var_val_len, u_char * statP, oid * name, size_t name_len) { static struct dlmod *dlm; if (var_val_type != ASN_OCTET_STR) { snmp_log(LOG_ERR, "write to dlmodName not ASN_OCTET_STR\n"); return SNMP_ERR_WRONGTYPE; } if (var_val_len > sizeof(dlm->name)) { snmp_log(LOG_ERR, "write to dlmodName: bad length\n"); return SNMP_ERR_WRONGLENGTH; } if (action == COMMIT) { dlm = dlmod_get_by_index(name[12]); if (!dlm || dlm->status == DLMOD_LOADED) return SNMP_ERR_RESOURCEUNAVAILABLE; strncpy(dlm->name, (const char *) var_val, var_val_len); dlm->name[var_val_len] = 0; } return SNMP_ERR_NOERROR; } int write_dlmodPath(int action, u_char * var_val, u_char var_val_type, size_t var_val_len, u_char * statP, oid * name, size_t name_len) { static struct dlmod *dlm; if (var_val_type != ASN_OCTET_STR) { snmp_log(LOG_ERR, "write to dlmodPath not ASN_OCTET_STR\n"); return SNMP_ERR_WRONGTYPE; } if (var_val_len > sizeof(dlm->path)) { snmp_log(LOG_ERR, "write to dlmodPath: bad length\n"); return SNMP_ERR_WRONGLENGTH; } if (action == COMMIT) { dlm = dlmod_get_by_index(name[12]); if (!dlm || dlm->status == DLMOD_LOADED) return SNMP_ERR_RESOURCEUNAVAILABLE; strncpy(dlm->path, (const char *) var_val, var_val_len); dlm->path[var_val_len] = 0; } return SNMP_ERR_NOERROR; } int write_dlmodStatus(int action, u_char * var_val, u_char var_val_type, size_t var_val_len, u_char * statP, oid * name, size_t name_len) { /* * variables we may use later */ struct dlmod *dlm; if (var_val_type != ASN_INTEGER) { snmp_log(LOG_ERR, "write to dlmodStatus not ASN_INTEGER\n"); return SNMP_ERR_WRONGTYPE; } if (var_val_len > sizeof(long)) { snmp_log(LOG_ERR, "write to dlmodStatus: bad length\n"); return SNMP_ERR_WRONGLENGTH; } if (action == COMMIT) { /* * object identifier in form .1.3.6.1.4.1.2021.13.14.2.1.4.x * where X is index with offset 12 */ dlm = dlmod_get_by_index(name[12]); switch (*((long *) var_val)) { case DLMOD_CREATE: if (dlm || (name[12] != dlmod_next_index)) return SNMP_ERR_RESOURCEUNAVAILABLE; dlm = dlmod_create_module(); if (!dlm) return SNMP_ERR_RESOURCEUNAVAILABLE; break; case DLMOD_LOAD: if (!dlm || dlm->status == DLMOD_LOADED) return SNMP_ERR_RESOURCEUNAVAILABLE; dlmod_load_module(dlm); break; case DLMOD_UNLOAD: if (!dlm || dlm->status != DLMOD_LOADED) return SNMP_ERR_RESOURCEUNAVAILABLE; dlmod_unload_module(dlm); break; case DLMOD_DELETE: if (!dlm || dlm->status == DLMOD_LOADED) return SNMP_ERR_RESOURCEUNAVAILABLE; dlmod_delete_module(dlm); break; default: return SNMP_ERR_WRONGVALUE; } } return SNMP_ERR_NOERROR; } #else /* no dlopen support */ void init_dlmod(void) { DEBUGMSGTL(("dlmod", "Dynamic modules not support on this platform\n")); } #endif