📄 request_list.c
字号:
* When walking over the request list, all of the per-request * magic is done here. */static int refresh_request(REQUEST *request, void *data){ rl_walk_t *info = (rl_walk_t *) data; time_t difference; child_pid_t child_pid; rad_assert(request->magic == REQUEST_MAGIC); /* * If the request is marked as a delayed reject, AND it's * time to send the reject, then do so now. */ if (request->finished && ((request->options & RAD_REQUEST_OPTION_DELAYED_REJECT) != 0)) { rad_assert(request->child_pid == NO_SUCH_CHILD_PID); difference = info->now - request->timestamp; if (difference >= (time_t) mainconfig.reject_delay) { /* * Clear the 'delayed reject' bit, so that we * don't do this again. */ request->options &= ~RAD_REQUEST_OPTION_DELAYED_REJECT; rad_send(request->reply, request->packet, request->secret); } } /* * If the request has finished processing, AND it's child has * been cleaned up, AND it's time to clean up the request, * OR, it's an accounting request. THEN, go delete it. * * If this is a request which had the "don't cache" option * set, then delete it immediately, as it CANNOT have a * duplicate. */ if (request->finished && ((request->timestamp + mainconfig.cleanup_delay <= info->now) || ((request->options & RAD_REQUEST_OPTION_DONT_CACHE) != 0))) { rad_assert(request->child_pid == NO_SUCH_CHILD_PID); /* * Request completed, delete it, and unlink it * from the currently 'alive' list of requests. */ DEBUG2("Cleaning up request %d ID %d with timestamp %08lx", request->number, request->packet->id, (unsigned long) request->timestamp); /* * Delete the request. */ rl_delete(request); return RL_WALK_CONTINUE; } /* * Maybe the child process handling the request has hung: * kill it, and continue. */ if ((request->timestamp + mainconfig.max_request_time) <= info->now) { int number; child_pid = request->child_pid; number = request->number; /* * There MUST be a RAD_PACKET reply. */ rad_assert(request->reply != NULL); /* * If we've tried to proxy the request, and * the proxy server hasn't responded, then * we send a REJECT back to the caller. * * For safety, we assert that there is no child * handling the request. If the assertion fails, * it means that we've sent a proxied request to * the home server, and the child thread is still * sitting on the request! */ if (request->proxy && !request->proxy_reply) { rad_assert(request->child_pid == NO_SUCH_CHILD_PID); radlog(L_ERR, "Rejecting request %d due to lack of any response from home server %s:%d", request->number, client_name(request->packet->src_ipaddr), request->packet->src_port); request_reject(request); request->finished = TRUE; return RL_WALK_CONTINUE; } if (mainconfig.kill_unresponsive_children) { if (child_pid != NO_SUCH_CHILD_PID) { /* * This request seems to have hung * - kill it */#ifdef HAVE_PTHREAD_H radlog(L_ERR, "Killing unresponsive thread for request %d", request->number); pthread_cancel(child_pid);#endif } /* else no proxy reply, quietly fail */ /* * Maybe we haven't killed it. In that * case, print a warning. */ } else if ((child_pid != NO_SUCH_CHILD_PID) && ((request->options & RAD_REQUEST_OPTION_LOGGED_CHILD) == 0)) { radlog(L_ERR, "WARNING: Unresponsive child (id %lu) for request %d", (unsigned long)child_pid, number); /* * Set the option that we've sent a log message, * so that we don't send more than one message * per request. */ request->options |= RAD_REQUEST_OPTION_LOGGED_CHILD; } /* * Send a reject message for the request, mark it * finished, and forget about the child. */ request_reject(request); request->child_pid = NO_SUCH_CHILD_PID; if (mainconfig.kill_unresponsive_children) request->finished = TRUE; return RL_WALK_CONTINUE; } /* the request has been in the queue for too long */ /* * If the request is still being processed, then due to the * above check, it's still within it's time limit. In that * case, don't do anything. */ if (request->child_pid != NO_SUCH_CHILD_PID) { return RL_WALK_CONTINUE; } /* * The request is finished. */ if (request->finished) goto setup_timeout; /* * We're not proxying requests at all. */ if (!mainconfig.proxy_requests) goto setup_timeout; /* * We're proxying synchronously, so we don't retry it here. * Some other code takes care of retrying the proxy requests. */ if (mainconfig.proxy_synchronous) goto setup_timeout; /* * The proxy retry delay is zero, meaning don't retry. */ if (mainconfig.proxy_retry_delay == 0) goto setup_timeout; /* * There is no proxied request for this packet, so there's * no proxy retries. */ if (!request->proxy) goto setup_timeout; /* * We've already seen the proxy reply, so we don't need * to send another proxy request. */ if (request->proxy_reply) goto setup_timeout; /* * It's not yet time to re-send this proxied request. */ if (request->proxy_next_try > info->now) goto setup_timeout; /* * If the proxy retry count is zero, then * we've sent the last try, and have NOT received * a reply from the end server. In that case, * we don't bother trying again, but just mark * the request as finished, and go to the next one. */ if (request->proxy_try_count == 0) { rad_assert(request->child_pid == NO_SUCH_CHILD_PID); request_reject(request); realm_disable(request->proxy->dst_ipaddr,request->proxy->dst_port); request->finished = TRUE; goto setup_timeout; } /* * We're trying one more time, so count down * the tries, and set the next try time. */ request->proxy_try_count--; request->proxy_next_try = info->now + mainconfig.proxy_retry_delay; /* Fix up Acct-Delay-Time */ if (request->proxy->code == PW_ACCOUNTING_REQUEST) { VALUE_PAIR *delaypair; delaypair = pairfind(request->proxy->vps, PW_ACCT_DELAY_TIME); if (!delaypair) { delaypair = paircreate(PW_ACCT_DELAY_TIME, PW_TYPE_INTEGER); if (!delaypair) { radlog(L_ERR|L_CONS, "no memory"); exit(1); } pairadd(&request->proxy->vps, delaypair); } delaypair->lvalue = info->now - request->proxy->timestamp; /* Must recompile the valuepairs to wire format */ free(request->proxy->data); request->proxy->data = NULL; } /* proxy accounting request */ /* * Assert that we have NOT seen the proxy reply yet. * * If we HAVE seen it, then we SHOULD NOT be bugging the * home server! */ rad_assert(request->proxy_reply == NULL); /* * Send the proxy packet. */ request->proxy_outstanding++; rad_send(request->proxy, NULL, request->proxysecret);setup_timeout: /* * Don't do more long-term checks, if we've got to wake * up now. */ if (info->smallest == 0) { return RL_WALK_CONTINUE; } /* * The request is finished. Wake up when it's time to * clean it up. */ if (request->finished) { difference = (request->timestamp + mainconfig.cleanup_delay) - info->now; /* * If the request is marked up to be rejected later, * then wake up later. */ if ((request->options & RAD_REQUEST_OPTION_DELAYED_REJECT) != 0) { if (difference >= (time_t) mainconfig.reject_delay) { difference = (time_t) mainconfig.reject_delay; } } } else if (request->proxy && !request->proxy_reply) { /* * The request is NOT finished, but there is an * outstanding proxy request, with no matching * proxy reply. * * Wake up when it's time to re-send * the proxy request. * * But in synchronous proxy, we don't retry but we update * the next retry time as NAS has not resent the request * in the given retry window. */ if (mainconfig.proxy_synchronous) { /* * If the retry_delay * count has passed, * then mark the realm dead. */ if (info->now > (request->timestamp + (mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count))) { rad_assert(request->child_pid == NO_SUCH_CHILD_PID); request_reject(request); realm_disable(request->proxy->dst_ipaddr, request->proxy->dst_port); request->finished = TRUE; goto setup_timeout; } request->proxy_next_try = info->now + mainconfig.proxy_retry_delay; } difference = request->proxy_next_try - info->now; } else { /* * The request is NOT finished. * * Wake up when it's time to kill the errant * thread/process. */ difference = (request->timestamp + mainconfig.max_request_time) - info->now; } /* * If the server is CPU starved, then we CAN miss a time * for servicing requests. In which case the 'difference' * value will be negative. select() doesn't like that, * so we fix it. */ if (difference < 0) { difference = 0; } /* * Update the 'smallest' time. */ if ((info->smallest < 0) || (difference < info->smallest)) { info->smallest = difference; } return RL_WALK_CONTINUE;}/* * Clean up the request list, every so often. * * This is done by walking through ALL of the list, and * - marking any requests which are finished, and expired * - killing any processes which are NOT finished after a delay * - deleting any marked requests. */struct timeval *rl_clean_list(time_t now){ /* * Static variables, so that we don't do all of this work * more than once per second. * * Note that we have 'tv' and 'last_tv'. 'last_tv' is * pointed to by 'last_tv_ptr', and depending on the * system implementation of select(), it MAY be modified. * * In that was, we want to use the ORIGINAL value, from * 'tv', and wipe out the (possibly modified) last_tv. */ static time_t last_cleaned_list = 0; static struct timeval tv, *last_tv_ptr = NULL; static struct timeval last_tv; rl_walk_t info; info.now = now; info.smallest = -1; /* * If we've already set up the timeout or cleaned the * request list this second, then don't do it again. We * simply return the sleep delay from last time. * * Note that if we returned NULL last time, there was nothing * to do. BUT we've been woken up since then, which can only * happen if we received a packet. And if we've received a * packet, then there's some work to do in the future. * * FIXME: We can probably use gettimeofday() for finer clock * resolution, as the current method will cause it to sleep * too long... */ if ((last_tv_ptr != NULL) && (last_cleaned_list == now) && (tv.tv_sec != 0)) { int i; /* * If we're NOT walking the entire request list, * then we want to iteratively check the request * list. * * If there is NO previous request, go look for one. */ if (!last_request) last_request = rl_next(last_request); /* * On average, there will be one request per * 'cleanup_delay' requests, which needs to be * serviced. * * And only do this servicing, if we have a request * to service. */ if (last_request) for (i = 0; i < mainconfig.cleanup_delay; i++) { REQUEST *next; /* * This function call MAY delete the * request pointed to by 'last_request'. */ next = rl_next(last_request); refresh_request(last_request, &info); last_request = next; /* * Nothing to do any more, exit. */ if (!last_request) break; } last_tv = tv; DEBUG2("Waking up in %d seconds...", (int) last_tv_ptr->tv_sec); return last_tv_ptr; } last_cleaned_list = now; last_request = NULL; DEBUG2("--- Walking the entire request list ---"); /* * Hmmm... this is Big Magic. We make it seem like * there's an additional second to wait, for a whole * host of reasons which I can't explain adequately, * but which cause the code to Just Work Right. */ info.now--; rl_walk(refresh_request, &info); /* * We haven't found a time at which we need to wake up. * Return NULL, so that the select() call will sleep forever. */ if (info.smallest < 0) { /* * If we're not proxying, then there really isn't anything * to do. * * If we ARE proxying, then we can safely sleep * forever if we're told to NEVER send proxy retries * ourselves, until the NAS kicks us again. * * Otherwise, there are no outstanding requests, then * we can sleep forever. This happens when we get * woken up with a bad packet. It's discarded, so if * there are no live requests, we can safely sleep * forever. */ if ((!mainconfig.proxy_requests) || mainconfig.proxy_synchronous || (rl_num_requests() == 0)) { DEBUG2("Nothing to do. Sleeping until we see a request."); last_tv_ptr = NULL; return NULL; } /* * We ARE proxying. In that case, we avoid a race condition * where a child thread handling a request proxies the * packet, and sets the retry delay. In that case, we're * supposed to wake up in N seconds, but we can't, as * we're sleeping forever. * * Instead, we prevent the problem by waking up anyhow * at the 'proxy_retry_delay' time, even if there's * nothing to do. In the worst case, this will cause * the server to wake up every N seconds, to do a small * amount of unnecessary work. */ info.smallest = mainconfig.proxy_retry_delay; } /* * Set the time (in seconds) for how long we're * supposed to sleep. */ tv.tv_sec = info.smallest; tv.tv_usec = 0; DEBUG2("Waking up in %d seconds...", (int) info.smallest); /* * Remember how long we should sleep for. */ last_tv = tv; last_tv_ptr = &last_tv; return last_tv_ptr;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -