net-snmp
5.4.1
|
00001 #include <net-snmp/net-snmp-config.h> 00002 00003 #if HAVE_STRING_H 00004 #include <string.h> 00005 #else 00006 #include <strings.h> 00007 #endif 00008 00009 #include <net-snmp/net-snmp-includes.h> 00010 #include <net-snmp/agent/net-snmp-agent-includes.h> 00011 00012 #include <net-snmp/agent/row_merge.h> 00013 00027 netsnmp_mib_handler * 00028 netsnmp_get_row_merge_handler(int prefix_len) 00029 { 00030 netsnmp_mib_handler *ret = NULL; 00031 ret = netsnmp_create_handler("row_merge", 00032 netsnmp_row_merge_helper_handler); 00033 if (ret) { 00034 ret->myvoid = (void *)(intptr_t)prefix_len; 00035 } 00036 return ret; 00037 } 00038 00041 int 00042 netsnmp_register_row_merge(netsnmp_handler_registration *reginfo) 00043 { 00044 netsnmp_inject_handler(reginfo, 00045 netsnmp_get_row_merge_handler(reginfo->rootoid_len+1)); 00046 return netsnmp_register_handler(reginfo); 00047 } 00048 00049 static void 00050 _rm_status_free(void *mem) 00051 { 00052 netsnmp_row_merge_status *rm_status = (netsnmp_row_merge_status*)mem; 00053 00054 if (NULL != rm_status->saved_requests) 00055 free(rm_status->saved_requests); 00056 00057 if (NULL != rm_status->saved_status) 00058 free(rm_status->saved_status); 00059 00060 free(mem); 00061 } 00062 00063 00066 netsnmp_row_merge_status * 00067 netsnmp_row_merge_status_get(netsnmp_handler_registration *reginfo, 00068 netsnmp_agent_request_info *reqinfo, 00069 int create_missing) 00070 { 00071 netsnmp_row_merge_status *rm_status; 00072 char buf[64]; 00073 int rc; 00074 00075 /* 00076 * see if we've already been here 00077 */ 00078 rc = snprintf(buf, sizeof(buf), "row_merge:%p", reginfo); 00079 if ((-1 == rc) || (rc >= sizeof(buf))) { 00080 snmp_log(LOG_ERR,"error creating key\n"); 00081 return NULL; 00082 } 00083 00084 rm_status = netsnmp_agent_get_list_data(reqinfo, buf); 00085 if ((NULL == rm_status) && create_missing) { 00086 void *data_list; 00087 00088 rm_status = SNMP_MALLOC_TYPEDEF(netsnmp_row_merge_status); 00089 if (NULL == rm_status) { 00090 snmp_log(LOG_ERR,"error allocating memory\n"); 00091 return NULL; 00092 } 00093 data_list = netsnmp_create_data_list(buf, rm_status, 00094 _rm_status_free); 00095 if (NULL == data_list) { 00096 free(rm_status); 00097 return NULL; 00098 } 00099 netsnmp_agent_add_list_data(reqinfo, data_list); 00100 } 00101 00102 return rm_status; 00103 } 00104 00109 int 00110 netsnmp_row_merge_status_first(netsnmp_handler_registration *reginfo, 00111 netsnmp_agent_request_info *reqinfo) 00112 { 00113 netsnmp_row_merge_status *rm_status; 00114 00115 /* 00116 * find status 00117 */ 00118 rm_status = netsnmp_row_merge_status_get(reginfo, reqinfo, 0); 00119 if (NULL == rm_status) 00120 return 0; 00121 00122 return (rm_status->count == 1) ? 1 : (rm_status->current == 1); 00123 } 00124 00129 int 00130 netsnmp_row_merge_status_last(netsnmp_handler_registration *reginfo, 00131 netsnmp_agent_request_info *reqinfo) 00132 { 00133 netsnmp_row_merge_status *rm_status; 00134 00135 /* 00136 * find status 00137 */ 00138 rm_status = netsnmp_row_merge_status_get(reginfo, reqinfo, 0); 00139 if (NULL == rm_status) 00140 return 0; 00141 00142 return (rm_status->count == 1) ? 1 : 00143 (rm_status->current == rm_status->rows); 00144 } 00145 00146 00147 #define ROW_MERGE_WAITING 0 00148 #define ROW_MERGE_ACTIVE 1 00149 #define ROW_MERGE_DONE 2 00150 #define ROW_MERGE_HEAD 3 00151 00153 int 00154 netsnmp_row_merge_helper_handler(netsnmp_mib_handler *handler, 00155 netsnmp_handler_registration *reginfo, 00156 netsnmp_agent_request_info *reqinfo, 00157 netsnmp_request_info *requests) 00158 { 00159 netsnmp_request_info *request, **saved_requests; 00160 char *saved_status; 00161 netsnmp_row_merge_status *rm_status; 00162 int i, j, ret, tail, count, final_rc = SNMP_ERR_NOERROR; 00163 00164 /* 00165 * Use the prefix length as supplied during registration, rather 00166 * than trying to second-guess what the MIB implementer wanted. 00167 */ 00168 int SKIP_OID = (int)(intptr_t)handler->myvoid; 00169 00170 DEBUGMSGTL(("helper:row_merge", "Got request (%d): ", SKIP_OID)); 00171 DEBUGMSGOID(("helper:row_merge", reginfo->rootoid, reginfo->rootoid_len)); 00172 DEBUGMSG(("helper:row_merge", "\n")); 00173 00174 /* 00175 * find or create status 00176 */ 00177 rm_status = netsnmp_row_merge_status_get(reginfo, reqinfo, 1); 00178 00179 /* 00180 * Count the requests, and set up an array to keep 00181 * track of the original order. 00182 */ 00183 for (count = 0, request = requests; request; request = request->next) { 00184 DEBUGIF("helper:row_merge") { 00185 DEBUGMSGTL(("helper:row_merge", " got varbind: ")); 00186 DEBUGMSGOID(("helper:row_merge", request->requestvb->name, 00187 request->requestvb->name_length)); 00188 DEBUGMSG(("helper:row_merge", "\n")); 00189 } 00190 count++; 00191 } 00192 00193 /* 00194 * Optimization: skip all this if there is just one request 00195 */ 00196 if(count == 1) { 00197 rm_status->count = count; 00198 if (requests->processed) 00199 return SNMP_ERR_NOERROR; 00200 return netsnmp_call_next_handler(handler, reginfo, reqinfo, requests); 00201 } 00202 00203 /* 00204 * we really should only have to do this once, instead of every pass. 00205 * as a precaution, we'll do it every time, but put in some asserts 00206 * to see if we have to. 00207 */ 00208 /* 00209 * if the count changed, re-do everything 00210 */ 00211 if ((0 != rm_status->count) && (rm_status->count != count)) { 00212 /* 00213 * ok, i know next/bulk can cause this condition. Probably 00214 * GET, too. need to rethink this mode counting. maybe 00215 * add the mode to the rm_status structure? xxx-rks 00216 */ 00217 if ((reqinfo->mode != MODE_GET) && 00218 (reqinfo->mode != MODE_GETNEXT) && 00219 (reqinfo->mode != MODE_GETBULK)) { 00220 netsnmp_assert((NULL != rm_status->saved_requests) && 00221 (NULL != rm_status->saved_status)); 00222 } 00223 DEBUGMSGTL(("helper:row_merge", "count changed! do over...\n")); 00224 00225 SNMP_FREE(rm_status->saved_requests); 00226 SNMP_FREE(rm_status->saved_status); 00227 00228 rm_status->count = 0; 00229 rm_status->rows = 0; 00230 } 00231 00232 if (0 == rm_status->count) { 00233 /* 00234 * allocate memory for saved structure 00235 */ 00236 rm_status->saved_requests = 00237 (netsnmp_request_info**)calloc(count+1, 00238 sizeof(netsnmp_request_info*)); 00239 rm_status->saved_status = (char*)calloc(count,sizeof(char)); 00240 } 00241 00242 saved_status = rm_status->saved_status; 00243 saved_requests = rm_status->saved_requests; 00244 00245 /* 00246 * set up saved requests, and set any processed requests to done 00247 */ 00248 i = 0; 00249 for (request = requests; request; request = request->next, i++) { 00250 if (request->processed) { 00251 saved_status[i] = ROW_MERGE_DONE; 00252 DEBUGMSGTL(("helper:row_merge", " skipping processed oid: ")); 00253 DEBUGMSGOID(("helper:row_merge", request->requestvb->name, 00254 request->requestvb->name_length)); 00255 DEBUGMSG(("helper:row_merge", "\n")); 00256 } 00257 else 00258 saved_status[i] = ROW_MERGE_WAITING; 00259 if (0 != rm_status->count) 00260 netsnmp_assert(saved_requests[i] == request); 00261 saved_requests[i] = request; 00262 saved_requests[i]->prev = NULL; 00263 } 00264 saved_requests[i] = NULL; 00265 00266 /* 00267 * Note that saved_requests[count] is valid 00268 * (because of the 'count+1' in the calloc above), 00269 * but NULL (since it's past the end of the list). 00270 * This simplifies the re-linking later. 00271 */ 00272 00273 /* 00274 * Work through the (unprocessed) requests in order. 00275 * For each of these, search the rest of the list for any 00276 * matching indexes, and link them into a new list. 00277 */ 00278 for (i=0; i<count; i++) { 00279 if (saved_status[i] != ROW_MERGE_WAITING) 00280 continue; 00281 00282 if (0 == rm_status->count) 00283 rm_status->rows++; 00284 DEBUGMSGTL(("helper:row_merge", " row %d oid[%d]: ", rm_status->rows, i)); 00285 DEBUGMSGOID(("helper:row_merge", saved_requests[i]->requestvb->name, 00286 saved_requests[i]->requestvb->name_length)); 00287 DEBUGMSG(("helper:row_merge", "\n")); 00288 00289 saved_requests[i]->next = NULL; 00290 saved_status[i] = ROW_MERGE_HEAD; 00291 tail = i; 00292 for (j=i+1; j<count; j++) { 00293 if (saved_status[j] != ROW_MERGE_WAITING) 00294 continue; 00295 00296 DEBUGMSGTL(("helper:row_merge", "? oid[%d]: ", j)); 00297 DEBUGMSGOID(("helper:row_merge", 00298 saved_requests[j]->requestvb->name, 00299 saved_requests[j]->requestvb->name_length)); 00300 if (!snmp_oid_compare( 00301 saved_requests[i]->requestvb->name+SKIP_OID, 00302 saved_requests[i]->requestvb->name_length-SKIP_OID, 00303 saved_requests[j]->requestvb->name+SKIP_OID, 00304 saved_requests[j]->requestvb->name_length-SKIP_OID)) { 00305 DEBUGMSG(("helper:row_merge", " match\n")); 00306 saved_requests[tail]->next = saved_requests[j]; 00307 saved_requests[j]->next = NULL; 00308 saved_requests[j]->prev = saved_requests[tail]; 00309 saved_status[j] = ROW_MERGE_ACTIVE; 00310 tail = j; 00311 } 00312 else 00313 DEBUGMSG(("helper:row_merge", " no match\n")); 00314 } 00315 } 00316 00317 /* 00318 * not that we have a list for each row, call next handler... 00319 */ 00320 if (0 == rm_status->count) 00321 rm_status->count = count; 00322 rm_status->current = 0; 00323 for (i=0; i<count; i++) { 00324 if (saved_status[i] != ROW_MERGE_HEAD) 00325 continue; 00326 00327 /* 00328 * found the head of a new row, 00329 * call the next handler with this list 00330 */ 00331 rm_status->current++; 00332 ret = netsnmp_call_next_handler(handler, reginfo, reqinfo, 00333 saved_requests[i]); 00334 if (ret != SNMP_ERR_NOERROR) { 00335 snmp_log(LOG_WARNING, 00336 "bad rc (%d) from next handler in row_merge\n", ret); 00337 if (SNMP_ERR_NOERROR == final_rc) 00338 final_rc = ret; 00339 } 00340 } 00341 00342 /* 00343 * restore original linked list 00344 */ 00345 for (i=0; i<count; i++) { 00346 saved_requests[i]->next = saved_requests[i+1]; 00347 if (i>0) 00348 saved_requests[i]->prev = saved_requests[i-1]; 00349 } 00350 00351 return final_rc; 00352 } 00353 00359 void 00360 netsnmp_init_row_merge(void) 00361 { 00362 netsnmp_register_handler_by_name("row_merge", 00363 netsnmp_get_row_merge_handler(-1)); 00364 }