1 |
/* This file is part of GNU RADIUS. |
/* This file is part of GNU Radius. |
2 |
Copyright (C) 2000,2001 Sergey Poznyakoff |
Copyright (C) 2002,2003 Sergey Poznyakoff |
3 |
|
|
4 |
This program is free software; you can redistribute it and/or modify |
GNU Radius is free software; you can redistribute it and/or modify |
5 |
it under the terms of the GNU General Public License as published by |
it under the terms of the GNU General Public License as published by |
6 |
the Free Software Foundation; either version 2 of the License, or |
the Free Software Foundation; either version 2 of the License, or |
7 |
(at your option) any later version. |
(at your option) any later version. |
8 |
|
|
9 |
This program is distributed in the hope that it will be useful, |
GNU Radius is distributed in the hope that it will be useful, |
10 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 |
GNU General Public License for more details. |
GNU General Public License for more details. |
13 |
|
|
14 |
You should have received a copy of the GNU General Public License |
You should have received a copy of the GNU General Public License |
15 |
along with this program; if not, write to the Free Software Foundation, |
along with GNU Radius; if not, write to the Free Software Foundation, |
16 |
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ |
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ |
17 |
|
|
18 |
#define RADIUS_MODULE_REQUEST_C |
#define RADIUS_MODULE_REQUEST_C |
19 |
|
|
|
#ifndef lint |
|
|
static char rcsid[] = |
|
|
"@(#) $Id$"; |
|
|
#endif |
|
|
|
|
20 |
#ifdef HAVE_CONFIG_H |
#ifdef HAVE_CONFIG_H |
21 |
# include <config.h> |
# include <config.h> |
22 |
#endif |
#endif |
23 |
|
#include <sys/types.h> |
24 |
|
#include <errno.h> |
25 |
#include <radiusd.h> |
#include <radiusd.h> |
26 |
|
#include <radsql.h> |
27 |
|
#include <list.h> |
28 |
|
|
29 |
extern struct request_class request_class[]; |
struct request_class request_class[] = { |
30 |
|
{ "AUTH", 0, MAX_REQUEST_TIME, CLEANUP_DELAY, |
31 |
|
radius_req_decode, /* Decoder */ |
32 |
|
radius_respond, /* Handler */ |
33 |
|
radius_req_xmit, /* Retransmitter */ |
34 |
|
radius_req_cmp, /* Comparator */ |
35 |
|
radius_req_free, /* Deallocator */ |
36 |
|
radius_req_drop, /* Drop function */ |
37 |
|
rad_sql_cleanup, /* Cleanup function */ |
38 |
|
radius_req_failure, /* Failure indicator */ |
39 |
|
}, |
40 |
|
{ "ACCT", 0, MAX_REQUEST_TIME, CLEANUP_DELAY, |
41 |
|
radius_req_decode, /* Decoder */ |
42 |
|
radius_respond, /* Handler */ |
43 |
|
radius_req_xmit, /* Retransmitter */ |
44 |
|
radius_req_cmp, /* Comparator */ |
45 |
|
radius_req_free, /* Deallocator */ |
46 |
|
radius_req_drop, /* Drop function */ |
47 |
|
rad_sql_cleanup, /* Cleanup function */ |
48 |
|
radius_req_failure, /* Failure indicator */ |
49 |
|
}, |
50 |
|
{ "PROXY",0, MAX_REQUEST_TIME, CLEANUP_DELAY, |
51 |
|
radius_req_decode, /* Decoder */ |
52 |
|
rad_proxy, /* Handler */ |
53 |
|
radius_req_xmit, /* Retransmitter */ |
54 |
|
radius_req_cmp, /* Comparator */ |
55 |
|
radius_req_free, /* Deallocator */ |
56 |
|
proxy_retry, /* Drop function */ |
57 |
|
NULL, /* Cleanup function */ |
58 |
|
NULL, /* Failure indicator */ |
59 |
|
}, |
60 |
|
#ifdef USE_SNMP |
61 |
|
{ "SNMP", 0, MAX_REQUEST_TIME, 0, |
62 |
|
snmp_decode, /* Decoder */ |
63 |
|
snmp_answer, /* Handler */ |
64 |
|
NULL, /* Retransmitter */ |
65 |
|
snmp_req_cmp, /* Comparator */ |
66 |
|
snmp_req_free, /* Deallocator */ |
67 |
|
snmp_req_drop, /* Drop function */ |
68 |
|
NULL, /* Cleanup function */ |
69 |
|
NULL, /* Failure indicator */ |
70 |
|
}, |
71 |
|
#endif |
72 |
|
{ NULL, } |
73 |
|
}; |
74 |
|
|
75 |
/* the request queue */ |
static LIST *request_list; /* List of REQUEST structures */ |
|
static REQUEST *first_request; |
|
|
pthread_mutex_t request_list_mutex = PTHREAD_MUTEX_INITIALIZER; |
|
|
|
|
|
#define request_list_block() Pthread_mutex_lock(&request_list_mutex) |
|
|
#define request_list_unblock() Pthread_mutex_unlock(&request_list_mutex) |
|
|
|
|
|
static void request_free(REQUEST *req); |
|
|
static void request_drop(int type, void *data, void *orig_data, int fd, |
|
|
char *status_str); |
|
|
static void request_xmit(int type, int code, void *data, int fd); |
|
|
static void request_cleanup(int type, void *data); |
|
|
static void *request_thread0(void *arg); |
|
|
static int request_process_command(); |
|
|
|
|
|
static pthread_mutex_t request_mutex = PTHREAD_MUTEX_INITIALIZER; |
|
|
static pthread_cond_t request_cond = PTHREAD_COND_INITIALIZER; |
|
|
|
|
|
static request_thread_command_fp request_command; |
|
|
static void *request_command_arg; |
|
|
static int request_command_count; |
|
76 |
|
|
77 |
int |
|
78 |
request_start_thread() |
/* ************************* General-purpose functions ********************* */ |
|
{ |
|
|
pthread_t tid; |
|
|
int rc = pthread_create(&tid, &thread_attr, request_thread0, NULL); |
|
|
if (rc) { |
|
|
radlog(L_ERR, _("Can't spawn new thread: %s"), |
|
|
strerror(rc)); |
|
|
return -1; |
|
|
} |
|
|
num_threads++; |
|
|
debug(1, ("started new thread: %x", (u_long) tid)); |
|
|
return 0; |
|
|
} |
|
79 |
|
|
80 |
void |
REQUEST * |
81 |
rad_cleanup_thread0(arg) |
request_create(int type, int fd, struct sockaddr_in *sa, |
82 |
void *arg; |
u_char *buf, size_t bufsize) |
|
{ |
|
|
filter_cleanup(); |
|
|
num_threads--; |
|
|
} |
|
|
|
|
|
void * |
|
|
request_thread0(arg) |
|
|
void *arg; |
|
83 |
{ |
{ |
84 |
radiusd_thread_init(); |
void *data; |
85 |
|
REQUEST *req; |
86 |
|
|
87 |
pthread_cleanup_push(rad_cleanup_thread0, NULL); |
if (request_class[type].decode(sa, buf, bufsize, &data)) |
88 |
while (1) { |
return NULL; |
89 |
REQUEST *req; |
req = emalloc(sizeof *req); |
90 |
|
req->data = data; |
91 |
request_process_command(); |
time(&req->timestamp); |
92 |
|
req->type = type; |
93 |
while (req = request_get()) |
req->addr = *sa; |
94 |
request_handle(req); |
req->rawdata = emalloc(bufsize); |
95 |
Pthread_mutex_lock(&request_mutex); |
memcpy(req->rawdata, buf, bufsize); |
96 |
debug(1,("thread waiting")); |
req->rawsize = bufsize; |
97 |
pthread_cond_wait(&request_cond, &request_mutex); |
req->child_id = 0; |
98 |
debug(1,("thread waken up")); |
req->status = RS_WAITING; |
99 |
Pthread_mutex_unlock(&request_mutex); |
req->fd = fd; |
100 |
} |
return req; |
|
/*NOTREACHED*/ |
|
|
pthread_cleanup_pop(1); |
|
|
return NULL; |
|
101 |
} |
} |
102 |
|
|
103 |
void |
void |
104 |
request_signal() |
request_free(REQUEST *req) |
|
{ |
|
|
Pthread_mutex_lock(&request_mutex); |
|
|
debug(100,("Signalling")); |
|
|
pthread_cond_signal(&request_cond); |
|
|
Pthread_mutex_unlock(&request_mutex); |
|
|
} |
|
|
|
|
|
int |
|
|
request_process_command() |
|
105 |
{ |
{ |
106 |
if (request_command_count > 0 && request_command) { |
if (req) { |
107 |
(*request_command)(request_command_arg); |
request_class[req->type].free(req->data); |
108 |
request_command_count--; |
efree(req->rawdata); |
109 |
return 1; |
efree(req); |
110 |
} |
} |
|
return 0; |
|
111 |
} |
} |
112 |
|
|
113 |
void |
void |
114 |
request_thread_command(fun, data) |
request_drop(int type, void *data, void *orig_data, int fd, char *status_str) |
|
request_thread_command_fp fun; |
|
|
void *data; |
|
115 |
{ |
{ |
116 |
Pthread_mutex_lock(&request_mutex); |
request_class[type].drop(type, data, orig_data, fd, status_str); |
|
request_command = fun; |
|
|
request_command_arg = data; |
|
|
request_command_count = num_threads; |
|
|
debug(100,("Signalling")); |
|
|
pthread_cond_broadcast(&request_cond); |
|
|
Pthread_mutex_unlock(&request_mutex); |
|
117 |
} |
} |
118 |
|
|
119 |
void |
int |
120 |
request_free(req) |
request_respond(REQUEST *req) |
|
REQUEST *req; |
|
121 |
{ |
{ |
122 |
request_class[req->type].free(req->data); |
return request_class[req->type].respond(req); |
|
mem_free(req); |
|
123 |
} |
} |
124 |
|
|
125 |
void |
void |
126 |
request_drop(type, data, orig_data, fd, status_str) |
request_xmit(REQUEST *req) |
|
int type; |
|
|
void *data; |
|
|
void *orig_data; |
|
|
int fd; |
|
|
char *status_str; |
|
127 |
{ |
{ |
128 |
request_class[type].drop(type, data, orig_data, fd, status_str); |
if (request_class[req->type].xmit) |
129 |
} |
request_class[req->type].xmit(req); |
|
|
|
|
void |
|
|
request_xmit(type, code, data, fd) |
|
|
int type; |
|
|
int code; |
|
|
void *data; |
|
|
int fd; |
|
|
{ |
|
|
if (request_class[type].xmit) |
|
|
request_class[type].xmit(type, code, data, fd); |
|
|
|
|
|
switch (type) { |
|
|
case R_AUTH: |
|
|
stat_inc(auth, ((RADIUS_REQ*)data)->ipaddr, num_dup_req); |
|
|
break; |
|
|
case R_ACCT: |
|
|
stat_inc(acct, ((RADIUS_REQ*)data)->ipaddr, num_dup_req); |
|
|
} |
|
130 |
} |
} |
131 |
|
|
132 |
int |
int |
133 |
request_cmp(type, a, b) |
request_cmp(int type, void *a, void *b) |
|
int type; |
|
|
void *a, *b; |
|
134 |
{ |
{ |
135 |
return request_class[type].comp(a, b); |
return request_class[type].comp(a, b); |
136 |
} |
} |
137 |
|
|
138 |
void |
void |
139 |
request_cleanup(type, data) |
request_cleanup(int type, void *data) |
|
int type; |
|
|
void *data; |
|
140 |
{ |
{ |
141 |
if (request_class[type].cleanup) |
if (request_class[type].cleanup) |
142 |
request_class[type].cleanup(type, data); |
request_class[type].cleanup(type, data); |
143 |
} |
} |
144 |
|
|
145 |
/* Request lifecycle: |
struct request_closure { |
146 |
A request starts its life in RS_WAITING state. It remains in this |
int type; /* Type of the request */ |
147 |
state until either it is picked up by some thread for processing |
void *data; /* Request contents */ |
148 |
or time-to-live seconds expire. When a thread starts processing |
time_t curtime; /* Current timestamp */ |
149 |
the request, it changes its state to RS_PENDING. When processing |
/* Output: */ |
150 |
of the request is finished, its state is changed to RS_COMPLETED. |
size_t request_count; /* Total number of requests */ |
151 |
If the request in RS_WAITING state does not switch to RS_PENDING |
size_t request_type_count; /* Number of requests of this type */ |
152 |
within time-to-live seconds, this request is removed from the queue. |
}; |
153 |
If the request in RS_PENDING state does not switch to RS_COMPLETED |
|
154 |
state within time-to-live seconds, it changes its state to RS_HUNG. |
static int |
155 |
If a request remains in RS_HUNG state for more than time-to-live seconds, |
_request_iterator(void *item, void *data) |
156 |
it changes its state to RS_DEAD and its handling thread is cancelled. |
{ |
157 |
If everything goes well, the thread being cancelled will shift |
REQUEST *req = item; |
158 |
the request into RS_COMPLETED state and it will be eventually removed |
struct request_closure *rp = data; |
159 |
from the queue. Otherwise, if the thread is stuck for good and the |
|
160 |
request remains in RS_DEAD state for more than time-to-live seconds |
if (req->status == RS_COMPLETED) { |
161 |
the daemon tries to restart itself. |
if (req->timestamp + request_class[req->type].cleanup_delay |
162 |
|
<= rp->curtime) { |
163 |
Finally, a request in RS_COMPLETED state remains in queue for |
debug(1, ("deleting completed %s request", |
164 |
request-cleanup-delay seconds, after which it is removed. */ |
request_class[req->type].name)); |
165 |
|
list_remove_current(request_list); |
166 |
REQUEST * |
request_free(req); |
167 |
request_put(type, data, activefd, numpending) |
return 0; |
168 |
int type; |
} |
169 |
void *data; |
} else if (req->timestamp + request_class[req->type].ttl |
170 |
int activefd; |
<= rp->curtime) { |
171 |
unsigned *numpending; |
|
172 |
{ |
radlog(L_NOTICE, |
173 |
REQUEST *curreq; |
_("Killing unresponsive %s child %lu"), |
174 |
REQUEST *prevreq; |
request_class[req->type].name, |
175 |
REQUEST *to_replace; |
(unsigned long) req->child_id); |
176 |
time_t curtime; |
rpp_kill(req->child_id, SIGKILL); |
177 |
int request_count, request_type_count; |
rpp_remove(req->child_id); |
178 |
|
return 0; |
179 |
time(&curtime); |
} |
|
request_count = request_type_count = 0; |
|
|
curreq = first_request; |
|
|
prevreq = NULL; |
|
|
to_replace = NULL; |
|
|
*numpending = 0; |
|
|
|
|
|
/* Block asynchronous access to the list */ |
|
|
request_list_block(); |
|
|
|
|
|
while (curreq != NULL) { |
|
|
|
|
|
if (curreq->status == RS_PENDING) |
|
|
++*numpending; |
|
|
|
|
|
if (curreq->status == RS_COMPLETED) { |
|
|
if (curreq->timestamp + |
|
|
request_class[curreq->type].cleanup_delay |
|
|
<= curtime) { |
|
|
debug(1, ("deleting completed %s request", |
|
|
request_class[curreq->type].name)); |
|
|
if (prevreq == NULL) { |
|
|
first_request = curreq->next; |
|
|
request_free(curreq); |
|
|
curreq = first_request; |
|
|
} else { |
|
|
prevreq->next = curreq->next; |
|
|
request_free(curreq); |
|
|
curreq = prevreq->next; |
|
|
} |
|
|
continue; |
|
|
} |
|
|
} else if (curreq->timestamp + |
|
|
request_class[curreq->type].ttl <= curtime) { |
|
|
switch (curreq->status) { |
|
|
case RS_WAITING: |
|
|
request_drop(curreq->type, NULL, curreq->data, |
|
|
curreq->fd, |
|
|
_("request timed out in queue")); |
|
|
|
|
|
if (prevreq == NULL) { |
|
|
first_request = curreq->next; |
|
|
request_free(curreq); |
|
|
curreq = first_request; |
|
|
} else { |
|
|
prevreq->next = curreq->next; |
|
|
request_free(curreq); |
|
|
curreq = prevreq->next; |
|
|
} |
|
|
break; |
|
|
|
|
|
case RS_PENDING: |
|
|
radlog(L_NOTICE, |
|
|
_("thread %d (%s) did not respond within %d seconds"), |
|
|
curreq->child_id, |
|
|
request_class[curreq->type].name, |
|
|
request_class[curreq->type].ttl); |
|
|
curreq->timestamp = curtime; |
|
|
curreq->status = RS_HUNG; |
|
|
prevreq = curreq; |
|
|
curreq = curreq->next; |
|
|
break; |
|
|
|
|
|
case RS_HUNG: |
|
|
radlog(L_NOTICE, |
|
|
_("Killing unresponsive %s thread %d"), |
|
|
request_class[curreq->type].name, |
|
|
curreq->child_id); |
|
|
curreq->status = RS_DEAD; |
|
|
/*FIXME: This causes much grief */ |
|
|
pthread_cancel(curreq->child_id); |
|
|
curreq->timestamp = curtime; |
|
|
prevreq = curreq; |
|
|
curreq = curreq->next; |
|
|
break; |
|
|
|
|
|
case RS_DEAD: |
|
|
radiusd_primitive_restart(0); |
|
|
} |
|
|
continue; |
|
|
} |
|
|
|
|
|
if (curreq->type == type |
|
|
&& request_cmp(type, curreq->data, data) == 0) { |
|
|
/* This is a duplicate request. |
|
|
If the handling process has already finished -- |
|
|
retransmit its results, if possible. |
|
|
Otherwise drop the request. */ |
|
|
if (curreq->status == RS_COMPLETED) |
|
|
request_xmit(type, |
|
|
curreq->child_return, |
|
|
curreq->data, |
|
|
activefd); |
|
|
else |
|
|
request_drop(type, data, curreq->data, |
|
|
curreq->fd, |
|
|
_("duplicate request")); |
|
|
request_list_unblock(); |
|
|
|
|
|
return NULL; |
|
|
} else { |
|
|
if (curreq->type == type) { |
|
|
request_type_count++; |
|
|
if (type != R_PROXY |
|
|
&& curreq->status == RS_COMPLETED |
|
|
&& (to_replace == NULL |
|
|
|| to_replace->timestamp > |
|
|
curreq->timestamp)) |
|
|
to_replace = curreq; |
|
|
} |
|
|
request_count++; |
|
|
prevreq = curreq; |
|
|
curreq = curreq->next; |
|
|
} |
|
|
} |
|
180 |
|
|
181 |
/* This is a new request */ |
if (!rp->data) |
182 |
if (request_count >= max_requests) { |
return 0; |
183 |
if (!to_replace) { |
|
184 |
request_drop(type, data, NULL, |
if (req->type == rp->type |
185 |
activefd, |
&& request_cmp(req->type, req->data, data) == 0) { |
186 |
_("too many requests in queue")); |
/* This is a duplicate request. If it is already completed, |
187 |
|
then hand it over to the child. |
188 |
request_list_unblock(); |
Otherwise drop the request. */ |
189 |
return NULL; |
if (req->status == RS_COMPLETED) { |
190 |
} |
if (radiusd_master()) |
191 |
} else if (request_class[type].max_requests |
rpp_forward_request(req); |
192 |
&& request_type_count >= request_class[type].max_requests) { |
else |
193 |
if (!to_replace) { |
request_xmit(req); |
194 |
request_drop(type, data, NULL, |
|
195 |
activefd, |
} else |
196 |
_("too many requests of this type")); |
request_drop(req->type, data, req->data, req->fd, |
197 |
|
_("duplicate request")); |
198 |
request_list_unblock(); |
/* Indicate end of request processing */ |
199 |
return NULL; |
rp->data = NULL; |
200 |
} |
return 0; |
201 |
} else |
} else { |
202 |
to_replace = NULL; |
if (req->type == rp->type) |
203 |
|
rp->request_type_count++; |
204 |
/* |
rp->request_count++; |
|
* Add this request to the list |
|
|
*/ |
|
|
if (to_replace == NULL) { |
|
|
curreq = mem_alloc(sizeof *curreq); |
|
|
curreq->next = first_request; |
|
|
first_request = curreq; |
|
|
} else { |
|
|
debug(1, ("replacing request dated %s", |
|
|
ctime(&to_replace->timestamp))); |
|
|
|
|
|
request_class[to_replace->type].free(to_replace->data); |
|
|
curreq = to_replace; |
|
205 |
} |
} |
206 |
|
return 0; |
|
curreq->timestamp = curtime; |
|
|
curreq->type = type; |
|
|
curreq->data = data; |
|
|
curreq->child_id = 0; |
|
|
curreq->status = RS_WAITING; |
|
|
curreq->fd = activefd; |
|
|
|
|
|
debug(1, ("%s request %lu added to the list. %d requests held.", |
|
|
request_class[type].name, |
|
|
(u_long) curreq->child_id, |
|
|
request_count+1)); |
|
|
|
|
|
request_list_unblock(); |
|
|
|
|
|
return curreq; |
|
|
} |
|
|
|
|
|
REQUEST * |
|
|
request_get() |
|
|
{ |
|
|
REQUEST *curreq; |
|
|
|
|
|
request_list_block(); |
|
|
for (curreq = first_request; curreq; curreq = curreq->next) |
|
|
if (curreq->status == RS_WAITING) { |
|
|
curreq->status = RS_PENDING; |
|
|
curreq->child_id = pthread_self(); |
|
|
time(&curreq->timestamp); |
|
|
break; |
|
|
} |
|
|
request_list_unblock(); |
|
|
return curreq; |
|
207 |
} |
} |
208 |
|
|
209 |
int |
int |
210 |
request_flush_list() |
request_handle(REQUEST *req, int (*handler)(REQUEST *)) |
211 |
{ |
{ |
212 |
REQUEST *curreq; |
struct request_closure rc; |
|
REQUEST *prevreq; |
|
|
time_t curtime; |
|
|
int request_count; |
|
|
|
|
|
time(&curtime); |
|
|
request_count = 0; |
|
|
curreq = first_request; |
|
|
prevreq = NULL; |
|
|
|
|
|
/* Block asynchronous access to the list |
|
|
*/ |
|
|
request_list_block(); |
|
|
|
|
|
while (curreq != NULL) { |
|
|
switch (curreq->status) { |
|
|
case RS_COMPLETED: |
|
|
case RS_WAITING: |
|
|
/* Request completed/waiting, delete it no matter how |
|
|
long does it reside in the queue */ |
|
|
debug(1, (curreq->status == RS_COMPLETED ? |
|
|
"deleting completed %s request" : |
|
|
"deleting waiting %s request", |
|
|
request_class[curreq->type].name)); |
|
|
if (prevreq == NULL) { |
|
|
first_request = curreq->next; |
|
|
request_free(curreq); |
|
|
curreq = first_request; |
|
|
} else { |
|
|
prevreq->next = curreq->next; |
|
|
request_free(curreq); |
|
|
curreq = prevreq->next; |
|
|
} |
|
|
break; |
|
213 |
|
|
214 |
case RS_PENDING: |
if (!req) |
215 |
if (curreq->timestamp + |
return 1; |
216 |
request_class[curreq->type].ttl <= curtime) { |
|
217 |
radlog(L_NOTICE, |
rc.type = req->type; |
218 |
_("thread %d (%s) did not respond within %d seconds"), |
rc.data = req->data; |
219 |
curreq->child_id, |
time(&rc.curtime); |
220 |
request_class[curreq->type].name, |
rc.request_count = rc.request_type_count = 0; |
221 |
request_class[curreq->type].ttl); |
|
222 |
curreq->timestamp = curtime; |
if (!request_list) |
223 |
curreq->status = RS_HUNG; |
request_list = list_create(); |
224 |
} |
else |
225 |
request_count++; |
list_iterate(request_list, _request_iterator, &rc); |
|
prevreq = curreq; |
|
|
curreq = curreq->next; |
|
|
break; |
|
|
|
|
|
case RS_HUNG: |
|
|
if (curreq->timestamp + |
|
|
request_class[curreq->type].ttl <= curtime) { |
|
|
radlog(L_NOTICE, |
|
|
_("Killing unresponsive %s thread %d"), |
|
|
request_class[curreq->type].name, |
|
|
curreq->child_id); |
|
|
curreq->status = RS_DEAD; |
|
|
/*FIXME: This causes much grief */ |
|
|
pthread_cancel(curreq->child_id); |
|
|
curreq->timestamp = curtime; |
|
|
} |
|
|
request_count++; |
|
|
prevreq = curreq; |
|
|
curreq = curreq->next; |
|
|
break; |
|
226 |
|
|
227 |
case RS_DEAD: |
if (!rc.data) |
228 |
radiusd_primitive_restart(0); |
return 1; |
229 |
break; |
|
230 |
|
/* This is a new request */ |
231 |
|
if (rc.request_count >= max_requests) { |
232 |
|
request_drop(req->type, req->data, NULL, req->fd, |
233 |
|
_("too many requests in queue")); |
234 |
|
return 1; |
235 |
|
} else if (request_class[req->type].max_requests |
236 |
|
&& rc.request_type_count >= request_class[req->type].max_requests) { |
237 |
|
request_drop(req->type, req->data, NULL, req->fd, |
238 |
|
_("too many requests of this type")); |
239 |
|
return 1; |
240 |
|
} |
241 |
|
|
242 |
default: |
/* Do we have free handlers? */ |
243 |
abort(); |
if (radiusd_master() && !rpp_ready()) { |
244 |
} |
radlog(L_NOTICE, _("Maximum number of children active")); |
245 |
} |
return 1; |
246 |
|
} |
247 |
|
|
248 |
|
/* Add request to the queue */ |
249 |
|
debug(1, ("%s request %lu added to the list. %d requests held.", |
250 |
|
request_class[req->type].name, |
251 |
|
(u_long) req->child_id, |
252 |
|
rc.request_count+1)); |
253 |
|
|
254 |
request_list_unblock(); |
list_append(request_list, req); |
255 |
return request_count; |
return (*handler)(req); |
256 |
} |
} |
257 |
|
|
258 |
int |
void |
259 |
request_stat_list(stat) |
request_set_status(pid_t pid, int status) |
|
QUEUE_STAT stat; |
|
260 |
{ |
{ |
261 |
REQUEST *curreq; |
REQUEST *p; |
|
|
|
|
memset(stat, 0, sizeof(QUEUE_STAT)); |
|
|
|
|
|
/* Block asynchronous access to the list |
|
|
*/ |
|
|
request_list_block(); |
|
|
for (curreq = first_request; curreq != NULL; curreq = curreq->next) { |
|
|
switch (curreq->status) { |
|
|
case RS_COMPLETED: |
|
|
stat[curreq->type].completed++; |
|
|
break; |
|
|
case RS_PENDING: |
|
|
stat[curreq->type].pending++; |
|
|
break; |
|
|
case RS_WAITING: |
|
|
stat[curreq->type].waiting++; |
|
|
break; |
|
|
} |
|
|
} |
|
|
request_list_unblock(); |
|
262 |
|
|
263 |
return 0; |
for (p = list_first(request_list); p; p = list_next(request_list)) |
264 |
|
if (p->child_id == pid) { |
265 |
|
p->status = status; |
266 |
|
break; |
267 |
|
} |
268 |
} |
} |
269 |
|
|
270 |
|
|
271 |
void * |
void |
272 |
request_scan_list(type, handler, closure) |
request_fail(int type, struct sockaddr_in *addr) |
273 |
int type; |
{ |
274 |
int (*handler)(); |
if (request_class[type].failure) |
275 |
void *closure; |
request_class[type].failure(type, addr); |
|
{ |
|
|
REQUEST *curreq; |
|
|
|
|
|
for (curreq = first_request; curreq; curreq = curreq->next) { |
|
|
if (curreq->type == type && |
|
|
handler(closure, curreq->data) == 0) |
|
|
return curreq->data; |
|
|
} |
|
|
return NULL; |
|
276 |
} |
} |
277 |
|
|
278 |
void |
static int |
279 |
rad_cleanup_thread(arg) |
_destroy_request(void *item, void *data) |
|
void *arg; |
|
280 |
{ |
{ |
281 |
REQUEST *curreq = arg; |
request_free((REQUEST*)item); |
282 |
debug(1, ("cleaning up request %lu", (u_long) curreq->child_id)); |
return 0; |
|
curreq->child_id = 0; |
|
|
curreq->status = RS_COMPLETED; |
|
|
time(&curreq->timestamp); |
|
|
request_cleanup(curreq->type, curreq->data); |
|
283 |
} |
} |
284 |
|
|
285 |
void |
void |
286 |
request_handle(req) |
request_init_queue() |
|
REQUEST *req; |
|
287 |
{ |
{ |
288 |
int rc = 0; |
list_destroy(&request_list, _destroy_request, NULL); |
289 |
|
} |
290 |
|
|
291 |
if (!req) |
void * |
292 |
return; |
request_scan_list(int type, list_iterator_t itr, void *closure) |
293 |
|
{ |
294 |
|
REQUEST *p; |
295 |
|
|
296 |
pthread_cleanup_push(rad_cleanup_thread, req); |
for (p = list_first(request_list); p; p = list_next(request_list)) { |
297 |
req->status = RS_PENDING; |
if (p->type == type |
298 |
req->child_id = pthread_self(); |
&& itr(p->data, closure) == 0) |
299 |
time(&req->timestamp); |
return p->data; |
300 |
debug(1, ("setup: %lu: %p", |
} |
301 |
(u_long) req->child_id, request_class[req->type].setup)); |
return NULL; |
|
if (request_class[req->type].setup) |
|
|
rc = request_class[req->type].setup(req); |
|
|
if (rc == 0) |
|
|
req->child_return = request_class[req->type].handler(req->data, |
|
|
req->fd); |
|
|
pthread_cleanup_pop(1); |
|
|
log_close(); |
|
|
debug(1, ("exiting")); |
|
302 |
} |
} |
|
|
|