📄 snmp.xs
字号:
else { count = perl_call_sv(sv, flags); } } LEAVE; return count;}/* Bulkwalk support routines *//* Add a context pointer to the list of valid pointers. Place it in the first** NULL slot in the array.*/static int_context_add(walk_context *context){ int i, j, new_sz; if ((i = _context_okay(context)) != 0) /* Already exists? Okay. */ return i; /* Initialize the array if necessary. */ if (_valid_contexts == NULL) { /* Create the _valid_contexts structure. */ Newz(0, _valid_contexts, 1, struct valid_contexts); assert(_valid_contexts != NULL); /* Populate the original valid contexts array. */ Newz(0, _valid_contexts->valid, 4, walk_context *); assert(_valid_contexts->valid != NULL); /* Computer number of slots in the array. */ _valid_contexts->sz_valid = sizeof(*_valid_contexts->valid) / sizeof(walk_context *); for (i = 0; i < _valid_contexts->sz_valid; i++) _valid_contexts->valid[i] = NULL; DBPRT(3, (DBOUT "Created valid_context array 0x%p (%d slots)\n", _valid_contexts->valid, _valid_contexts->sz_valid)); } /* Search through the list, looking for NULL's -- unused slots. */ for (i = 0; i < _valid_contexts->sz_valid; i++) if (_valid_contexts->valid[i] == NULL) break; /* Did we walk off the end of the list? Need to grow the list. Double ** it for now. */ if (i == _valid_contexts->sz_valid) { new_sz = _valid_contexts->sz_valid * 2; Renew(_valid_contexts->valid, new_sz, walk_context *); assert(_valid_contexts->valid != NULL); DBPRT(3, (DBOUT "Resized valid_context array 0x%p from %d to %d slots\n", _valid_contexts->valid, _valid_contexts->sz_valid, new_sz)); _valid_contexts->sz_valid = new_sz; /* Initialize the new half of the resized array. */ for (j = i; j < new_sz; j++) _valid_contexts->valid[j] = NULL; } /* Store the context pointer in the array and return 0 (success). */ _valid_contexts->valid[i] = context; DBPRT(3,( "Add context 0x%p to valid context list\n", context)); return 0;}/* Remove a context pointer from the valid list. Replace the pointer with** NULL in the valid pointer list.*/static int_context_del(walk_context *context){ int i; if (_valid_contexts == NULL) /* Make sure it was initialized. */ return 1; for (i = 0; i < _valid_contexts->sz_valid; i++) { if (_valid_contexts->valid[i] == context) { DBPRT(3,( "Remove context 0x%p from valid context list\n", context)); _valid_contexts->valid[i] = NULL; /* Remove it from the list. */ return 0; /* Return successful status. */ } } return 1;}/* Check if a specific context pointer is in the valid list. Return true (1)** if the context is still in the valid list, or 0 if not (or context is NULL).*/static int_context_okay(walk_context *context){ int i; if (_valid_contexts == NULL) /* Make sure it was initialized. */ return 0; if (context == NULL) /* Asked about a NULL context? Fail. */ return 0; for (i = 0; i < _valid_contexts->sz_valid; i++) if (_valid_contexts->valid[i] == context) return 1; /* Found it! */ return 0; /* No match -- return failure. */}/* Check if the walk is completed, based upon the context. Also set the** ignore flag on any completed variables -- this prevents them from being** being sent in later packets.*/static int_bulkwalk_done(walk_context *context){ int is_done = 1; int i; bulktbl *bt_entry; /* bulktbl requested OID entry */ /* Don't consider walk done until at least one packet has been exchanged. */ if (context->pkts_exch == 0) return 0; /* Fix up any requests that have completed. If the complete flag is set, ** or it is a non-repeater OID, set the ignore flag so that it will not ** be considered further. Assume we are done with the walk, and note ** otherwise if we aren't. Return 1 if all requests are complete, or 0 ** if there's more to do. */ for (i = 0; i < context->nreq_oids; i ++) { bt_entry = &context->req_oids[i]; if (bt_entry->complete || bt_entry->norepeat) { /* This request is complete. Remove it from list of ** walks still in progress. */ DBPRT(1, (DBOUT "Ignoring %s request oid %s\n", bt_entry->norepeat? "nonrepeater" : "completed", sprint_objid(_debugx, bt_entry->req_oid, bt_entry->req_len))); /* Ignore this OID in any further packets. */ bt_entry->ignore = 1; } /* If any OID is not being ignored, the walk is not done. Must loop ** through all requests to do the fixup -- no early return possible. */ if (!bt_entry->ignore) is_done = 0; } return is_done; /* Did the walk complete? */}/* Callback registered with SNMP. Return 1 from this callback to cause the** current request to be deleted from the retransmit queue.*/static int_bulkwalk_async_cb(int op, SnmpSession *ss, int reqid, struct snmp_pdu *pdu, void *context_ptr){ walk_context *context; int done = 0; int npushed; SV **err_str_svp; SV **err_num_svp; /* Handle callback request for asynchronous bulkwalk. If the bulkwalk has ** not completed, and has not timed out, send the next request packet in ** the walk. ** ** Return 0 to indicate success (caller ignores return value). */ DBPRT(2, (DBOUT "bulkwalk_async_cb(op %d, reqid 0x%08X, context 0x%p)\n", op, reqid, context_ptr)); context = (walk_context *)context_ptr; /* Make certain this is a valid context pointer. This pdu may ** have been retransmitted after the bulkwalk was completed ** (and the context was destroyed). If so, just return. */ if (!_context_okay(context)) { DBPRT(2,( "Ignoring PDU for dead context 0x%p...\n", context)); return 1; } /* Is this a retransmission of a request we've already seen or some ** unexpected request id? If so, just ignore it. */ if (reqid != context->exp_reqid) { DBPRT(2, ("Got reqid 0x%08X, expected reqid 0x%08X. Ignoring...\n", reqid, context->exp_reqid)); return 1; } /* Ignore any future packets for this reqid. */ context->exp_reqid = -1; err_str_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorStr", 8, 1); err_num_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorNum", 8, 1); switch (op) { case RECEIVED_MESSAGE: { DBPRT(1,( "Received message for reqid 0x%08X ...\n", reqid)); switch (pdu->command) { case SNMP_MSG_RESPONSE: { DBPRT(2, (DBOUT "Calling bulkwalk_recv_pdu(context 0x%p, pdu 0x%p)\n", context_ptr, pdu)); /* Handle the response PDU. If an error occurs or there were ** no variables in the response, consider the walk done. If ** the response was okay, check if we have any more to do after ** this response. */ if (_bulkwalk_recv_pdu(context, pdu) <= 0) done = 1; else done = _bulkwalk_done(context); /* Also set req ignore flags */ break; } default: { DBPRT(1,( "unexpected pdu->command %d\n", pdu->command)); done = 1; /* "This can't happen!", so bail out when it does. */ break; } } break; } case TIMED_OUT: { DBPRT(1,( "\n*** Timeout for reqid 0x%08X\n\n", reqid)); sv_setpv(*err_str_svp, (char*)snmp_api_errstring(SNMPERR_TIMEOUT)); sv_setiv(*err_num_svp, SNMPERR_TIMEOUT); /* Timeout means something bad has happened. Return a not-okay ** result to the async callback. */ npushed = _bulkwalk_finish(context, 0 /* NOT OKAY */); return 1; } default: { DBPRT(1,( "unexpected callback op %d\n", op)); sv_setpv(*err_str_svp, (char*)snmp_api_errstring(SNMPERR_GENERR)); sv_setiv(*err_num_svp, SNMPERR_GENERR); npushed = _bulkwalk_finish(context, 0 /* NOT OKAY */); return 1; } } /* We have either timed out, or received and parsed in a response. Now, ** if we have more variables to test, call bulkwalk_send_pdu() to enqueue ** another async packet, and return. ** ** If, however, the bulkwalk has completed (or an error has occurred that ** cuts the walk short), call bulkwalk_finish() to push the results onto ** the Perl call stack. Then explicitly call the Perl callback that was ** passed in by the user oh-so-long-ago. */ if (!done) { DBPRT(1,( "bulkwalk not complete -- send next pdu from callback\n")); if (_bulkwalk_send_pdu(context) != NULL) return 1; DBPRT(1,( "send_pdu() failed!\n")); /* Fall through and return what we have so far. */ } /* Call the perl callback with the return values and we're done. */ npushed = _bulkwalk_finish(context, 1 /* OKAY */); return 1;}static struct snmp_pdu *_bulkwalk_send_pdu(walk_context *context){ struct snmp_pdu *pdu = NULL; struct snmp_pdu *response = NULL; struct bulktbl *bt_entry; int nvars = 0; int reqid; int status; int i; /* Send a pdu requesting any remaining variables in the context. ** ** In synchronous mode, returns a pointer to the response packet. ** ** In asynchronous mode, it returns the request ID, cast to a struct snmp *, ** not a valid SNMP response packet. The async code should not be trying ** to get variables out of this "response". ** ** In either case, return a NULL pointer on error or failure. */ SV **sess_ptr_sv = hv_fetch((HV*)SvRV(context->sess_ref), "SessPtr", 7, 1); struct snmp_session *ss = (SnmpSession *)SvIV((SV*)SvRV(*sess_ptr_sv)); SV **err_str_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorStr", 8, 1); SV **err_num_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorNum", 8, 1); SV **err_ind_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorInd", 8, 1); /* Create a new PDU and send the remaining set of requests to the agent. */ pdu = snmp_pdu_create(SNMP_MSG_GETBULK); if (pdu == NULL) { sv_setpv(*err_str_svp, "snmp_pdu_create(GETBULK) failed: "); sv_catpv(*err_str_svp, strerror(errno)); sv_setiv(*err_num_svp, SNMPERR_MALLOC); goto err; } /* Request non-repeater variables only in the first packet exchange. */ pdu->errstat = (context->pkts_exch == 0) ? context->non_reps : 0; pdu->errindex = context->max_reps; for (i = 0; i < context->nreq_oids; i++) { bt_entry = &context->req_oids[i]; if (bt_entry->ignore) continue; assert(bt_entry->complete == 0); if (!snmp_add_null_var(pdu, bt_entry->last_oid, bt_entry->last_len)) { sv_setpv(*err_str_svp, "snmp_add_null_var() failed"); sv_setiv(*err_num_svp, SNMPERR_GENERR); sv_setiv(*err_ind_svp, i); goto err; } nvars ++; DBPRT(1, (DBOUT " Add %srepeater %s\n", bt_entry->norepeat ? "non" : "", sprint_objid(_debugx, bt_entry->last_oid, bt_entry->last_len))); } /* Make sure variables are actually being requested in the packet. */ assert (nvars != 0); context->pkts_exch ++; DBPRT(1, (DBOUT "Sending %ssynchronous request %d...\n", SvTRUE(context->perl_cb) ? "a" : "", context->pkts_exch)); /* We handle the asynchronous and synchronous requests differently here. ** For async, we simply enqueue the packet with a callback to handle the ** returned response, then return. Note that this we call the bulkwalk ** callback, and hand it the walk_context, not the Perl callback. The ** snmp_async_send() function returns the reqid on success, 0 on failure. */ if (SvTRUE(context->perl_cb)) { reqid = snmp_async_send(ss, pdu, _bulkwalk_async_cb, (void *)context); DBPRT(2,( "bulkwalk_send_pdu(): snmp_async_send => 0x%08X\n", reqid)); if (reqid == 0) { sv_setpv(*err_str_svp, (char*)snmp_api_errstring(ss->s_snmp_errno)); sv_setiv(*err_num_svp, ss->s_snmp_errno); goto err; } /* Make a note of the request we expect to be answered. */ context->exp_reqid = reqid; /* Callbacks take care of the rest. Let the caller know how many vars ** we sent in this request. Note that this is not a valid SNMP PDU, ** but that's because a response has not yet been received. */ return (struct snmp_pdu *)reqid; } /* This code is for synchronous mode support. ** ** Send the PDU and block awaiting the response. Return the response ** packet back to the caller. Note that snmp_sess_read() frees the pdu. */ status = __send_sync_pdu(ss, pdu, &response, NO_RETRY_NOSUCH, *err_str_svp, *err_num_svp, *err_ind_svp); pdu = NULL; /* Check for a failed request. __send_sync_pdu() will set the appropriate ** values in the error string and number SV's. */ if (status != STAT_SUCCESS) { DBPRT(1,( "__send_sync_pdu() -> %d\n",(int)status)); goto err; } DBPRT(1, (DBOUT "%d packets exchanged, response 0x%p\n", context->pkts_exch, response)); return response; err: if (pdu) snmp_free_pdu(pdu); return NULL;}/* Handle an incoming GETBULK response PDU. This function just pulls the** variables off of the PDU and builds up the arrays of returned values** that are stored in the context.**** Returns the number of variables found in this packet, or -1 on error.** Note that the caller is expected to free the pdu.*/static int_bulkwalk_recv_pdu(walk_context *context, struct snmp_pdu *pdu){ struct variable_list *vars; struct tree *tp; char type_str[MAX_TYPE_NAME_LEN]; char str_buf[STR_BUF_SIZE];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -