/* * Portions of this file are subject to the following copyright(s). See * the Net-SNMP's COPYING file for more details and other copyrights * that may apply: * * Portions of this file are copyrighted by: * Copyright (c) 2016 VMware, Inc. All rights reserved. * Use is subject to license terms specified in the COPYING file * distributed with the Net-SNMP package. */ #include #include #include #include #include #include #include #include #ifdef HAVE_DMALLOC_H static void free_wrapper(void * p) { free(p); } #else #define free_wrapper free #endif netsnmp_feature_provide(watcher_all) netsnmp_feature_child_of(watcher_all, mib_helpers) netsnmp_feature_child_of(watcher_create_info6, watcher_all) netsnmp_feature_child_of(watcher_register_timestamp, watcher_all) netsnmp_feature_child_of(watcher_ulong_scalar, watcher_all) netsnmp_feature_child_of(watcher_read_only_ulong_scalar, watcher_all) netsnmp_feature_child_of(watcher_read_only_int_scalar, watcher_all) netsnmp_feature_child_of(watcher_long_scalar, watcher_all) netsnmp_feature_child_of(watcher_read_only_long_scalar, watcher_all) netsnmp_feature_child_of(watcher_int_scalar, watcher_all) netsnmp_feature_child_of(read_only_counter32_scalar, watcher_all) netsnmp_feature_child_of(watcher_spinlock, watcher_all) /** @defgroup watcher watcher * Watch a specified variable and process it as an instance or scalar object * @ingroup leaf * @{ */ netsnmp_mib_handler * netsnmp_get_watcher_handler(void) { netsnmp_mib_handler *ret = NULL; ret = netsnmp_create_handler("watcher", netsnmp_watcher_helper_handler); if (ret) { ret->flags |= MIB_HANDLER_AUTO_NEXT; } return ret; } netsnmp_watcher_info * netsnmp_init_watcher_info6(netsnmp_watcher_info *winfo, void *data, size_t size, u_char type, int flags, size_t max_size, size_t* size_p) { winfo->data = data; winfo->data_size = size; winfo->max_size = max_size; winfo->type = type; winfo->flags = flags; winfo->data_size_p = size_p; return winfo; } #ifndef NETSNMP_FEATURE_REMOVE_WATCHER_CREATE_INFO6 netsnmp_watcher_info * netsnmp_create_watcher_info6(void *data, size_t size, u_char type, int flags, size_t max_size, size_t* size_p) { netsnmp_watcher_info *winfo = SNMP_MALLOC_TYPEDEF(netsnmp_watcher_info); if (winfo) netsnmp_init_watcher_info6(winfo, data, size, type, flags, max_size, size_p); return winfo; } #endif /* NETSNMP_FEATURE_REMOVE_WATCHER_CREATE_INFO6 */ netsnmp_watcher_info * netsnmp_init_watcher_info(netsnmp_watcher_info *winfo, void *data, size_t size, u_char type, int flags) { return netsnmp_init_watcher_info6(winfo, data, size, type, (flags ? flags : WATCHER_FIXED_SIZE), size, /* Probably wrong for non-fixed * size data */ NULL); } netsnmp_watcher_info * netsnmp_create_watcher_info(void *data, size_t size, u_char type, int flags) { netsnmp_watcher_info *winfo = SNMP_MALLOC_TYPEDEF(netsnmp_watcher_info); if (winfo) netsnmp_init_watcher_info(winfo, data, size, type, flags); return winfo; } /** * Register a watched scalar. The caller remains the owner of watchinfo. * * @see netsnmp_register_watched_instance2() */ int netsnmp_register_watched_instance(netsnmp_handler_registration *reginfo, netsnmp_watcher_info *watchinfo) { netsnmp_mib_handler *whandler = NULL; if (reginfo && watchinfo) { whandler = netsnmp_get_watcher_handler(); if (whandler) { whandler->myvoid = (void *)watchinfo; if (netsnmp_inject_handler(reginfo, whandler) == SNMPERR_SUCCESS) return netsnmp_register_instance(reginfo); } } snmp_log(LOG_ERR, "could not create watched instance handler\n"); netsnmp_handler_free(whandler); netsnmp_handler_registration_free(reginfo); return MIB_REGISTRATION_FAILED; } /** * Register a watched scalar. Ownership of watchinfo is transferred to the handler. * * @see netsnmp_register_watched_instance() */ int netsnmp_register_watched_instance2(netsnmp_handler_registration *reginfo, netsnmp_watcher_info *watchinfo) { netsnmp_mib_handler *whandler = NULL; if (reginfo && watchinfo) { whandler = netsnmp_get_watcher_handler(); if (whandler) { whandler->myvoid = (void *)watchinfo; netsnmp_owns_watcher_info(whandler); if (netsnmp_inject_handler(reginfo, whandler) == SNMPERR_SUCCESS) return netsnmp_register_instance(reginfo); } } snmp_log(LOG_ERR, "could not create watched instance2 handler\n"); netsnmp_handler_free(whandler); netsnmp_handler_registration_free(reginfo); return MIB_REGISTRATION_FAILED; } /** * Register a watched scalar. The caller remains the owner of watchinfo. * * @see netsnmp_register_watched_scalar2() */ int netsnmp_register_watched_scalar(netsnmp_handler_registration *reginfo, netsnmp_watcher_info *watchinfo) { netsnmp_mib_handler *whandler = NULL; if (reginfo && watchinfo) { whandler = netsnmp_get_watcher_handler(); if (whandler) { whandler->myvoid = (void *)watchinfo; if (netsnmp_inject_handler(reginfo, whandler) == SNMPERR_SUCCESS) return netsnmp_register_scalar(reginfo); } } snmp_log(LOG_ERR, "could not create watched scalar handler\n"); netsnmp_handler_free(whandler); netsnmp_handler_registration_free(reginfo); return MIB_REGISTRATION_FAILED; } /** * Register a watched scalar. Ownership of watchinfo is transferred to the handler. * * @see netsnmp_register_watched_scalar() */ int netsnmp_register_watched_scalar2(netsnmp_handler_registration *reginfo, netsnmp_watcher_info *watchinfo) { netsnmp_mib_handler *whandler = NULL; if (reginfo && watchinfo) { whandler = netsnmp_get_watcher_handler(); if (whandler) { whandler->myvoid = (void *)watchinfo; netsnmp_owns_watcher_info(whandler); if (netsnmp_inject_handler(reginfo, whandler) == SNMPERR_SUCCESS) return netsnmp_register_scalar(reginfo); } } snmp_log(LOG_ERR, "could not create watched scalar2 handler\n"); netsnmp_handler_free(whandler); netsnmp_handler_registration_free(reginfo); return MIB_REGISTRATION_FAILED; } void netsnmp_owns_watcher_info(netsnmp_mib_handler *handler) { netsnmp_assert(handler); netsnmp_assert(handler->myvoid); handler->data_clone = (void *(*)(void *))netsnmp_clone_watcher_info; handler->data_free = free; } /** @cond */ NETSNMP_STATIC_INLINE size_t get_data_size(const netsnmp_watcher_info* winfo) { if (winfo->flags & WATCHER_SIZE_STRLEN) return strlen((const char*)winfo->data); else { size_t res; if (winfo->flags & WATCHER_SIZE_IS_PTR) res = *winfo->data_size_p; else res = winfo->data_size; if (winfo->flags & WATCHER_SIZE_UNIT_OIDS) res *= sizeof(oid); return res; } } NETSNMP_STATIC_INLINE void set_data(netsnmp_watcher_info* winfo, void* data, size_t size) { memcpy(winfo->data, data, size); if (winfo->flags & WATCHER_SIZE_STRLEN) ((char*)winfo->data)[size] = '\0'; else { if (winfo->flags & WATCHER_SIZE_UNIT_OIDS) size /= sizeof(oid); if (winfo->flags & WATCHER_SIZE_IS_PTR) *winfo->data_size_p = size; else winfo->data_size = size; } } typedef struct { size_t size; char data[1]; } netsnmp_watcher_cache; NETSNMP_STATIC_INLINE netsnmp_watcher_cache* netsnmp_watcher_cache_create(const void* data, size_t size) { netsnmp_watcher_cache *res = (netsnmp_watcher_cache*) malloc(sizeof(netsnmp_watcher_cache) + size - 1); if (res) { res->size = size; memcpy(res->data, data, size); } return res; } /** @endcond */ int netsnmp_watcher_helper_handler(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { netsnmp_watcher_info *winfo = (netsnmp_watcher_info *) handler->myvoid; #ifndef NETSNMP_NO_WRITE_SUPPORT netsnmp_watcher_cache *old_data; #endif /* NETSNMP_NO_WRITE_SUPPORT */ DEBUGMSGTL(("helper:watcher", "Got request: %d\n", reqinfo->mode)); DEBUGMSGTL(( "helper:watcher", " oid:")); DEBUGMSGOID(("helper:watcher", requests->requestvb->name, requests->requestvb->name_length)); DEBUGMSG(( "helper:watcher", "\n")); switch (reqinfo->mode) { /* * data requests */ case MODE_GET: snmp_set_var_typed_value(requests->requestvb, winfo->type, winfo->data, get_data_size(winfo)); break; /* * SET requests. Should only get here if registered RWRITE */ #ifndef NETSNMP_NO_WRITE_SUPPORT case MODE_SET_RESERVE1: if (requests->requestvb->type != winfo->type) { netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_WRONGTYPE); handler->flags |= MIB_HANDLER_AUTO_NEXT_OVERRIDE_ONCE; } else if (((winfo->flags & WATCHER_MAX_SIZE) && requests->requestvb->val_len > winfo->max_size) || ((winfo->flags & WATCHER_FIXED_SIZE) && requests->requestvb->val_len != get_data_size(winfo))) { netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_WRONGLENGTH); handler->flags |= MIB_HANDLER_AUTO_NEXT_OVERRIDE_ONCE; } else if ((winfo->flags & WATCHER_SIZE_STRLEN) && (memchr(requests->requestvb->val.string, '\0', requests->requestvb->val_len) != NULL)) { netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_WRONGVALUE); handler->flags |= MIB_HANDLER_AUTO_NEXT_OVERRIDE_ONCE; } break; case MODE_SET_RESERVE2: /* * store old info for undo later */ old_data = netsnmp_watcher_cache_create(winfo->data, get_data_size(winfo)); if (old_data == NULL) { netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_RESOURCEUNAVAILABLE); handler->flags |= MIB_HANDLER_AUTO_NEXT_OVERRIDE_ONCE; } else netsnmp_request_add_list_data(requests, netsnmp_create_data_list ("watcher", old_data, &free_wrapper)); break; case MODE_SET_FREE: /* * nothing to do */ break; case MODE_SET_ACTION: /* * update current */ set_data(winfo, (void *)requests->requestvb->val.string, requests->requestvb->val_len); break; case MODE_SET_UNDO: old_data = (netsnmp_watcher_cache*)netsnmp_request_get_list_data(requests, "watcher"); set_data(winfo, old_data->data, old_data->size); break; case MODE_SET_COMMIT: break; #endif /* NETSNMP_NO_WRITE_SUPPORT */ default: snmp_log(LOG_ERR, "watcher handler called with an unknown mode: %d\n", reqinfo->mode); return SNMP_ERR_GENERR; } /* next handler called automatically - 'AUTO_NEXT' */ return SNMP_ERR_NOERROR; } /*************************** * * A specialised form of the above, reporting * the sysUpTime indicated by a given timestamp * ***************************/ netsnmp_mib_handler * netsnmp_get_watched_timestamp_handler(void) { netsnmp_mib_handler *ret = NULL; ret = netsnmp_create_handler("watcher-timestamp", netsnmp_watched_timestamp_handler); if (ret) { ret->flags |= MIB_HANDLER_AUTO_NEXT; } return ret; } int netsnmp_watched_timestamp_register(netsnmp_mib_handler *whandler, netsnmp_handler_registration *reginfo, marker_t timestamp) { if (reginfo && whandler && timestamp) { whandler->myvoid = (void *)timestamp; if (netsnmp_inject_handler(reginfo, whandler) == SNMPERR_SUCCESS) return netsnmp_register_scalar(reginfo); /* XXX - or instance? */ } snmp_log(LOG_ERR, "could not create watched timestamp handler\n"); netsnmp_handler_registration_free(reginfo); return MIB_REGISTRATION_FAILED; } #ifndef NETSNMP_FEATURE_REMOVE_WATCHER_REGISTER_TIMESTAMP int netsnmp_register_watched_timestamp(netsnmp_handler_registration *reginfo, marker_t timestamp) { netsnmp_mib_handler *whandler; whandler = netsnmp_get_watched_timestamp_handler(); return netsnmp_watched_timestamp_register(whandler, reginfo, timestamp); } #endif /* NETSNMP_FEATURE_REMOVE_WATCHER_REGISTER_TIMESTAMP */ int netsnmp_watched_timestamp_handler(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { marker_t timestamp = (marker_t) handler->myvoid; long uptime; DEBUGMSGTL(("helper:watcher:timestamp", "Got request: %d\n", reqinfo->mode)); DEBUGMSGTL(( "helper:watcher:timestamp", " oid:")); DEBUGMSGOID(("helper:watcher:timestamp", requests->requestvb->name, requests->requestvb->name_length)); DEBUGMSG(( "helper:watcher:timestamp", "\n")); switch (reqinfo->mode) { /* * data requests */ case MODE_GET: if (handler->flags & NETSNMP_WATCHER_DIRECT) uptime = * (long*)timestamp; else uptime = netsnmp_marker_uptime( timestamp ); snmp_set_var_typed_value(requests->requestvb, ASN_TIMETICKS, (u_char *) &uptime, sizeof(uptime)); break; /* * Timestamps are inherently Read-Only, * so don't need to support SET requests. */ #ifndef NETSNMP_NO_WRITE_SUPPORT case MODE_SET_RESERVE1: netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_NOTWRITABLE); handler->flags |= MIB_HANDLER_AUTO_NEXT_OVERRIDE_ONCE; return SNMP_ERR_NOTWRITABLE; #endif /* NETSNMP_NO_WRITE_SUPPORT */ } /* next handler called automatically - 'AUTO_NEXT' */ return SNMP_ERR_NOERROR; } /*************************** * * Another specialised form of the above, * implementing a 'TestAndIncr' spinlock * ***************************/ #ifndef NETSNMP_FEATURE_REMOVE_WATCHER_SPINLOCK netsnmp_mib_handler * netsnmp_get_watched_spinlock_handler(void) { netsnmp_mib_handler *ret = NULL; ret = netsnmp_create_handler("watcher-spinlock", netsnmp_watched_spinlock_handler); if (ret) { ret->flags |= MIB_HANDLER_AUTO_NEXT; } return ret; } int netsnmp_register_watched_spinlock(netsnmp_handler_registration *reginfo, int *spinlock) { netsnmp_mib_handler *whandler = NULL; netsnmp_watcher_info *winfo = NULL; if (reginfo && spinlock) { whandler = netsnmp_get_watched_spinlock_handler(); if (whandler) { whandler->myvoid = (void *)spinlock; winfo = netsnmp_create_watcher_info((void *)spinlock, sizeof(int), ASN_INTEGER, WATCHER_FIXED_SIZE); if (winfo && (netsnmp_inject_handler(reginfo, whandler) == SNMPERR_SUCCESS)) return netsnmp_register_watched_scalar2(reginfo, winfo); } } snmp_log(LOG_ERR, "could not create watched spinlock handler\n"); SNMP_FREE(winfo); netsnmp_handler_free(whandler); netsnmp_handler_registration_free(reginfo); return MIB_REGISTRATION_FAILED; } int netsnmp_watched_spinlock_handler(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { #ifndef NETSNMP_NO_WRITE_SUPPORT int *spinlock = (int *) handler->myvoid; netsnmp_request_info *request; #endif /* NETSNMP_NO_WRITE_SUPPORT */ DEBUGMSGTL(("helper:watcher:spinlock", "Got request: %d\n", reqinfo->mode)); DEBUGMSGTL(( "helper:watcher:spinlock", " oid:")); DEBUGMSGOID(("helper:watcher:spinlock", requests->requestvb->name, requests->requestvb->name_length)); DEBUGMSG(( "helper:watcher:spinlock", "\n")); switch (reqinfo->mode) { /* * Ensure the assigned value matches the current one */ #ifndef NETSNMP_NO_WRITE_SUPPORT case MODE_SET_RESERVE1: for (request=requests; request; request=request->next) { if (request->processed) continue; if (*request->requestvb->val.integer != *spinlock) { netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_WRONGVALUE); handler->flags |= MIB_HANDLER_AUTO_NEXT_OVERRIDE_ONCE; return SNMP_ERR_WRONGVALUE; } } break; /* * Everything else worked, so increment the spinlock */ case MODE_SET_COMMIT: (*spinlock)++; break; #endif /* NETSNMP_NO_WRITE_SUPPORT */ } /* next handler called automatically - 'AUTO_NEXT' */ return SNMP_ERR_NOERROR; } #endif /* NETSNMP_FEATURE_REMOVE_WATCHER_SPINLOCK */ /*************************** * * Convenience registration routines - modelled on * the equivalent netsnmp_register_*_instance() calls * ***************************/ netsnmp_watcher_info * netsnmp_clone_watcher_info(netsnmp_watcher_info *winfo) { netsnmp_watcher_info *copy = malloc(sizeof(*copy)); if (copy) *copy = *winfo; return copy; } static int register_scalar_watcher(const char* name, const oid* reg_oid, size_t reg_oid_len, void *data, size_t size, u_char type, Netsnmp_Node_Handler * subhandler, int mode) { netsnmp_handler_registration *reginfo = NULL; netsnmp_mib_handler *whandler = NULL; netsnmp_watcher_info* watchinfo; if (!name || !reg_oid || !data) return MIB_REGISTRATION_FAILED; watchinfo = netsnmp_create_watcher_info(data, size, type, WATCHER_FIXED_SIZE); if (watchinfo) { whandler = netsnmp_get_watcher_handler(); if (whandler) { whandler->myvoid = watchinfo; netsnmp_owns_watcher_info(whandler); reginfo = netsnmp_create_handler_registration(name, subhandler, reg_oid, reg_oid_len, mode); if (reginfo && (netsnmp_inject_handler(reginfo, whandler) == SNMPERR_SUCCESS)) return netsnmp_register_scalar(reginfo); } } snmp_log(LOG_ERR, "failed to register scalar watcher\n"); netsnmp_handler_free(whandler); SNMP_FREE(watchinfo); netsnmp_handler_registration_free(reginfo); return MIB_REGISTRATION_FAILED; } #ifndef NETSNMP_FEATURE_REMOVE_WATCHER_ULONG_SCALAR int netsnmp_register_ulong_scalar(const char *name, const oid * reg_oid, size_t reg_oid_len, u_long * it, Netsnmp_Node_Handler * subhandler) { return register_scalar_watcher( name, reg_oid, reg_oid_len, (void *)it, sizeof( u_long ), ASN_UNSIGNED, subhandler, HANDLER_CAN_RWRITE); } #endif /* NETSNMP_FEATURE_REMOVE_WATCHER_ULONG_SCALAR */ #ifndef NETSNMP_FEATURE_REMOVE_WATCHER_READ_ONLY_ULONG_SCALAR int netsnmp_register_read_only_ulong_scalar(const char *name, const oid * reg_oid, size_t reg_oid_len, u_long * it, Netsnmp_Node_Handler * subhandler) { return register_scalar_watcher( name, reg_oid, reg_oid_len, (void *)it, sizeof( u_long ), ASN_UNSIGNED, subhandler, HANDLER_CAN_RONLY); } #endif /* NETSNMP_FEATURE_REMOVE_WATCHER_READ_ONLY_ULONG_SCALAR */ #ifndef NETSNMP_FEATURE_REMOVE_WATCHER_LONG_SCALAR int netsnmp_register_long_scalar(const char *name, const oid * reg_oid, size_t reg_oid_len, long * it, Netsnmp_Node_Handler * subhandler) { return register_scalar_watcher( name, reg_oid, reg_oid_len, (void *)it, sizeof( long ), ASN_INTEGER, subhandler, HANDLER_CAN_RWRITE); } #endif /* NETSNMP_FEATURE_REMOVE_WATCHER_LONG_SCALAR */ #ifndef NETSNMP_FEATURE_REMOVE_WATCHER_READ_ONLY_LONG_SCALAR int netsnmp_register_read_only_long_scalar(const char *name, const oid * reg_oid, size_t reg_oid_len, long * it, Netsnmp_Node_Handler * subhandler) { return register_scalar_watcher( name, reg_oid, reg_oid_len, (void *)it, sizeof( long ), ASN_INTEGER, subhandler, HANDLER_CAN_RONLY); } #endif /* NETSNMP_FEATURE_REMOVE_WATCHER_READ_ONLY_LONG_SCALAR */ #ifndef NETSNMP_FEATURE_REMOVE_WATCHER_INT_SCALAR int netsnmp_register_int_scalar(const char *name, const oid * reg_oid, size_t reg_oid_len, int * it, Netsnmp_Node_Handler * subhandler) { return register_scalar_watcher( name, reg_oid, reg_oid_len, (void *)it, sizeof( int ), ASN_INTEGER, subhandler, HANDLER_CAN_RWRITE); } #endif /* NETSNMP_FEATURE_REMOVE_WATCHER_INT_SCALAR */ #ifndef NETSNMP_FEATURE_REMOVE_WATCHER_READ_ONLY_INT_SCALAR int netsnmp_register_read_only_int_scalar(const char *name, const oid * reg_oid, size_t reg_oid_len, int * it, Netsnmp_Node_Handler * subhandler) { return register_scalar_watcher( name, reg_oid, reg_oid_len, (void *)it, sizeof( int ), ASN_INTEGER, subhandler, HANDLER_CAN_RONLY); } #endif /* NETSNMP_FEATURE_REMOVE_WATCHER_READ_ONLY_INT_SCALAR */ #ifndef NETSNMP_FEATURE_REMOVE_READ_ONLY_COUNTER32_SCALAR int netsnmp_register_read_only_counter32_scalar(const char *name, const oid * reg_oid, size_t reg_oid_len, u_long * it, Netsnmp_Node_Handler * subhandler) { return register_scalar_watcher( name, reg_oid, reg_oid_len, (void *)it, sizeof( u_long ), ASN_COUNTER, subhandler, HANDLER_CAN_RONLY); } #endif /* NETSNMP_FEATURE_REMOVE_READ_ONLY_COUNTER32_SCALAR */ /** @} */