mirror of
https://github.com/isc-projects/bind9.git
synced 2026-06-08 23:52:05 -04:00
New function, omapi_listener_shutdown, to destroy all memory used by the
listener. Manage a list of all connections known by the listener, so they can be shut down by omapi_listener_shutdown.
This commit is contained in:
parent
fbf54b5ee2
commit
a89a8e3754
1 changed files with 221 additions and 65 deletions
|
|
@ -30,9 +30,16 @@
|
|||
|
||||
typedef struct omapi_listener_object {
|
||||
OMAPI_OBJECT_PREAMBLE;
|
||||
isc_mutex_t mutex;
|
||||
isc_task_t *task;
|
||||
isc_socket_t *socket; /* Connection socket. */
|
||||
isc_socket_t *socket; /* Listening socket. */
|
||||
isc_sockaddr_t address;
|
||||
/*
|
||||
* Locked by mutex.
|
||||
*/
|
||||
isc_boolean_t accepting;
|
||||
isc_condition_t waiter;
|
||||
ISC_LIST(omapi_connection_t) connections;
|
||||
} omapi_listener_t;
|
||||
|
||||
/*
|
||||
|
|
@ -47,21 +54,15 @@ listener_accept(isc_task_t *task, isc_event_t *event) {
|
|||
isc_socket_t *socket;
|
||||
omapi_connection_t *connection = NULL;
|
||||
omapi_object_t *protocol = NULL;
|
||||
omapi_listener_t *listener;
|
||||
|
||||
/*
|
||||
* XXXDCL What are the meaningful things the listen/accept function
|
||||
* can do if it fails to process an incoming connection because one
|
||||
* of the functions it calls fails?
|
||||
* The cleanup options are hurting my head.
|
||||
* XXXDCL audit error handling
|
||||
*/
|
||||
|
||||
/*
|
||||
* Immediately set up another listen task for the socket.
|
||||
*/
|
||||
isc_socket_accept(event->sender, task, listener_accept, event->arg);
|
||||
|
||||
result = ((isc_socket_newconnev_t *)event)->result;
|
||||
socket = ((isc_socket_newconnev_t *)event)->newsocket;
|
||||
listener = (omapi_listener_t *)event->arg;
|
||||
|
||||
/*
|
||||
* No more need for the event, once all the desired data has been
|
||||
|
|
@ -69,13 +70,32 @@ listener_accept(isc_task_t *task, isc_event_t *event) {
|
|||
*/
|
||||
isc_event_free(&event);
|
||||
|
||||
if (result == ISC_R_CANCELED) {
|
||||
/*
|
||||
* omapi_listener_shutdown was called. Stop accepting incoming
|
||||
* connection by not queuing another accept.
|
||||
*/
|
||||
LOCK(&listener->mutex);
|
||||
listener->accepting = ISC_FALSE;
|
||||
|
||||
SIGNAL(&listener->waiter);
|
||||
UNLOCK(&listener->mutex);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up another accept task for the socket.
|
||||
*/
|
||||
isc_socket_accept(listener->socket, task, listener_accept, listener);
|
||||
|
||||
/*
|
||||
* Check for the validity of new connection event.
|
||||
*/
|
||||
if (result != ISC_R_SUCCESS)
|
||||
/*
|
||||
* The result is probably ISC_R_UNEXPECTED; what can really be
|
||||
* done about this other than just flunking out of here?
|
||||
* The result is probably ISC_R_UNEXPECTED. What can really
|
||||
* be done about it other than just * flunking out of here?
|
||||
*/
|
||||
return;
|
||||
|
||||
|
|
@ -117,9 +137,6 @@ listener_accept(isc_task_t *task, isc_event_t *event) {
|
|||
ISC_LIST_INIT(connection->output_buffers);
|
||||
ISC_LIST_APPEND(connection->output_buffers, obuffer, link);
|
||||
|
||||
RUNTIME_CHECK(isc_mutex_init(&connection->mutex) == ISC_R_SUCCESS);
|
||||
RUNTIME_CHECK(isc_mutex_init(&connection->recv_lock) == ISC_R_SUCCESS);
|
||||
|
||||
/*
|
||||
* Create a new protocol object to oversee the handling of this
|
||||
* connection.
|
||||
|
|
@ -138,31 +155,34 @@ listener_accept(isc_task_t *task, isc_event_t *event) {
|
|||
OBJECT_REF(&connection->inner, protocol);
|
||||
|
||||
/*
|
||||
* Send the introductory message.
|
||||
*/
|
||||
result = send_intro(protocol, OMAPI_PROTOCOL_VERSION);
|
||||
|
||||
if (result != ISC_R_SUCCESS)
|
||||
goto free_protocol_object;
|
||||
|
||||
/*
|
||||
* Lose the external reference to the protocol object so
|
||||
* both the connection object and protocol object will
|
||||
* be freed when the connection ends.
|
||||
* Lose the external reference to the protocol object so both the
|
||||
* connection object and protocol object will be freed when the
|
||||
* connection ends.
|
||||
*/
|
||||
OBJECT_DEREF(&protocol);
|
||||
|
||||
/*
|
||||
* Add the connection to the list of connections known by the
|
||||
* listener. This is an added reference to the connection
|
||||
* object, but since there's no easy way to use omapi_object_reference
|
||||
* with the ISC_LIST macros, that reference is just not counted.
|
||||
*/
|
||||
ISC_LIST_APPEND(listener->connections, connection, link);
|
||||
|
||||
/*
|
||||
* Remember the listener that accepted the connection, so it
|
||||
* can be told when the connection goes away.
|
||||
*/
|
||||
OBJECT_REF(&connection->listener, listener);
|
||||
|
||||
/*
|
||||
* Send the introductory message. The return value does not
|
||||
* matter; if send_intro failed, it already destroyed the connection.
|
||||
*/
|
||||
(void)send_intro(connection->inner, OMAPI_PROTOCOL_VERSION);
|
||||
|
||||
return;
|
||||
|
||||
free_protocol_object:
|
||||
/*
|
||||
* Remove the protocol object's reference to the connection
|
||||
* object, so that the connection object will be destroyed.
|
||||
* XXXDCL I am not quite sure the right thing is being done here.
|
||||
*/
|
||||
OBJECT_DEREF(&protocol);
|
||||
|
||||
/* FALLTHROUGH */
|
||||
free_connection_object:
|
||||
/*
|
||||
* Destroy the connection. This will free everything created
|
||||
|
|
@ -189,6 +209,8 @@ omapi_listener_listen(omapi_object_t *caller, int port, int max) {
|
|||
omapi_listener_t *listener;
|
||||
struct in_addr inaddr;
|
||||
|
||||
REQUIRE(caller != NULL);
|
||||
|
||||
task = NULL;
|
||||
result = isc_task_create(omapi_taskmgr, NULL, 0, &task);
|
||||
if (result != ISC_R_SUCCESS)
|
||||
|
|
@ -208,11 +230,9 @@ omapi_listener_listen(omapi_object_t *caller, int port, int max) {
|
|||
|
||||
listener->task = task;
|
||||
|
||||
/*
|
||||
* Tie the listener object to the calling object.
|
||||
*/
|
||||
OBJECT_REF(&caller->outer, listener);
|
||||
OBJECT_REF(&listener->inner, caller);
|
||||
ISC_LIST_INIT(listener->connections);
|
||||
RUNTIME_CHECK(isc_mutex_init(&listener->mutex) == ISC_R_SUCCESS);
|
||||
RUNTIME_CHECK(isc_condition_init(&listener->waiter) == ISC_R_SUCCESS);
|
||||
|
||||
/*
|
||||
* Create a socket on which to listen.
|
||||
|
|
@ -225,6 +245,9 @@ omapi_listener_listen(omapi_object_t *caller, int port, int max) {
|
|||
/*
|
||||
* Set up the addressses on which to listen and bind to it.
|
||||
*/
|
||||
if (port == 0)
|
||||
port = OMAPI_PROTOCOL_PORT;
|
||||
|
||||
inaddr.s_addr = INADDR_ANY;
|
||||
isc_sockaddr_fromin(&listener->address, &inaddr, port);
|
||||
|
||||
|
|
@ -237,25 +260,137 @@ omapi_listener_listen(omapi_object_t *caller, int port, int max) {
|
|||
*/
|
||||
result = isc_socket_listen(listener->socket, max);
|
||||
|
||||
if (result == ISC_R_SUCCESS)
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
/*
|
||||
* Queue up the first accept event. The listener object
|
||||
* will be passed to listener_accept() when it is called,
|
||||
* though currently nothing is done with it.
|
||||
* will be passed to listener_accept() when it is called.
|
||||
*/
|
||||
listener->accepting = ISC_TRUE;
|
||||
result = isc_socket_accept(listener->socket, task,
|
||||
listener_accept, listener);
|
||||
}
|
||||
|
||||
if (result != ISC_R_SUCCESS)
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
/*
|
||||
* The listener has a refcnt of 2, so this does not really
|
||||
* free it. XXXDCL
|
||||
* Tie the listener object to the calling object.
|
||||
*/
|
||||
OBJECT_REF(&caller->outer, listener);
|
||||
OBJECT_REF(&listener->inner, caller);
|
||||
|
||||
} else
|
||||
/*
|
||||
* Failed to set up the listener.
|
||||
*/
|
||||
OBJECT_DEREF(&listener);
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
void
|
||||
omapi_listener_shutdown(omapi_object_t *listener) {
|
||||
omapi_listener_t *l;
|
||||
omapi_connection_t *c;
|
||||
isc_time_t timeout;
|
||||
isc_interval_t interval;
|
||||
isc_result_t result = ISC_R_SUCCESS;
|
||||
|
||||
REQUIRE((listener != NULL && listener->type == omapi_type_listener) ||
|
||||
(listener->outer != NULL &&
|
||||
listener->outer->type == omapi_type_listener));
|
||||
|
||||
if (listener->type == omapi_type_listener)
|
||||
l = (omapi_listener_t *)listener;
|
||||
else
|
||||
l = (omapi_listener_t *)listener->outer;
|
||||
|
||||
/*
|
||||
* It is improper to call this function without having had a successful
|
||||
* run of omapi_listener_listen.
|
||||
*/
|
||||
REQUIRE(l->socket != NULL && l->task != NULL);
|
||||
|
||||
/*
|
||||
* Stop accepting connections.
|
||||
*/
|
||||
isc_socket_cancel(l->socket, NULL, ISC_SOCKCANCEL_ACCEPT);
|
||||
|
||||
/*
|
||||
* All connections this listener was responsible for must be gone.
|
||||
* Since it is possible that this shutdown was triggered by one
|
||||
* of the clients, give it a little time to exit, as well as
|
||||
* allowing other connections to finish up cleanly. The
|
||||
* cancelled accept event also needs to be received before
|
||||
* the listener task, socket and object can be destroyed.
|
||||
*
|
||||
* isc_time_nowplusinterval returns an isc_result_t; anything other
|
||||
* than ISC_R_SUCCESS is wildly unexpected because the Unix
|
||||
* implementation uses gettimeofday(), which is documented to only
|
||||
* return an error if its argument is an invalid memory address, and
|
||||
* the Win32 implementation always returns ISC_R_SUCCESS. In any
|
||||
* event, if it fails, there is nothing to do but soldier on.
|
||||
* The waituntil would immediately timeout, and the connections
|
||||
* would be forcibly blown away.
|
||||
*
|
||||
* 5 seconds is an arbitrary constant.
|
||||
*/
|
||||
isc_interval_set(&interval, 5, 0);
|
||||
isc_time_nowplusinterval(&timeout, &interval);
|
||||
|
||||
LOCK(&l->mutex);
|
||||
|
||||
while (! ISC_LIST_EMPTY(l->connections) && result == ISC_R_SUCCESS) {
|
||||
ISC_UTIL_TRACE(fprintf(stderr, "WAIT %p LOCK %p %s %d\n",
|
||||
&l->waiter, &l->mutex,
|
||||
__FILE__, __LINE__));
|
||||
|
||||
result = isc_condition_waituntil(&l->waiter, &l->mutex,
|
||||
&timeout);
|
||||
|
||||
ISC_UTIL_TRACE(fprintf(stderr, "WAITED %p LOCKED %p %s %d\n",
|
||||
&l->waiter, &l->mutex,
|
||||
__FILE__, __LINE__));
|
||||
}
|
||||
|
||||
/*
|
||||
* If there are still some connections hanging about,
|
||||
* they won't be for long.
|
||||
*/
|
||||
for (c = ISC_LIST_HEAD(l->connections); c != NULL;
|
||||
c = ISC_LIST_NEXT(c, link))
|
||||
omapi_connection_disconnect((omapi_object_t *)c,
|
||||
OMAPI_FORCE_DISCONNECT);
|
||||
|
||||
/*
|
||||
* Again wait for any remaining connections to be destroyed, and
|
||||
* ensure the listen socket has received the cancelled accept event.
|
||||
* This will happen rapidly now because they were all cancelled.
|
||||
*/
|
||||
while (! ISC_LIST_EMPTY(l->connections) || l->accepting)
|
||||
WAIT(&l->waiter, &l->mutex);
|
||||
|
||||
/*
|
||||
* The accept cancel event should now have been posted,
|
||||
* and the connections should be gone.
|
||||
*/
|
||||
INSIST(! l->accepting && ISC_LIST_EMPTY(l->connections));
|
||||
|
||||
UNLOCK(&l->mutex);
|
||||
|
||||
/*
|
||||
* Break the link between the listener object and its parent
|
||||
* (usually a generic object); this is done so the server's
|
||||
* reference to its managing object does not prevent the listener
|
||||
* object from being destroyed.
|
||||
*/
|
||||
OBJECT_DEREF(&l->inner->outer);
|
||||
OBJECT_DEREF(&l->inner);
|
||||
|
||||
/*
|
||||
* The listener object can now be freed.
|
||||
*/
|
||||
OBJECT_DEREF(&l);
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
listener_setvalue(omapi_object_t *listener, omapi_string_t *name,
|
||||
omapi_data_t *value)
|
||||
|
|
@ -283,38 +418,59 @@ listener_getvalue(omapi_object_t *listener, omapi_string_t *name,
|
|||
}
|
||||
|
||||
static void
|
||||
listener_destroy(omapi_object_t *object) {
|
||||
omapi_listener_t *listener;
|
||||
listener_destroy(omapi_object_t *listener) {
|
||||
omapi_listener_t *l;
|
||||
|
||||
REQUIRE(object != NULL && object->type == omapi_type_listener);
|
||||
REQUIRE(listener != NULL && listener->type == omapi_type_listener);
|
||||
|
||||
listener = (omapi_listener_t *)object;
|
||||
l = (omapi_listener_t *)listener;
|
||||
|
||||
isc_task_destroy(&listener->task);
|
||||
INSIST(ISC_LIST_EMPTY(l->connections));
|
||||
|
||||
if (listener->socket != NULL) {
|
||||
#if 0 /*XXXDCL*/
|
||||
isc_socket_cancel(listener->socket, NULL, ISC_SOCKCANCEL_ALL);
|
||||
isc_socket_shutdown(listener->socket, ISC_SOCKSHUT_ALL);
|
||||
#else
|
||||
isc_task_shutdown(listener->task);
|
||||
#endif
|
||||
listener->socket = NULL;
|
||||
if (l->task != NULL) {
|
||||
isc_task_destroy(&l->task);
|
||||
l->task = NULL;
|
||||
}
|
||||
|
||||
if (l->socket != NULL) {
|
||||
isc_socket_detach(&l->socket);
|
||||
l->socket = NULL;
|
||||
}
|
||||
|
||||
RUNTIME_CHECK(isc_mutex_destroy(&l->mutex) == ISC_R_SUCCESS);
|
||||
RUNTIME_CHECK(isc_condition_destroy(&l->waiter) == ISC_R_SUCCESS);
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
listener_signalhandler(omapi_object_t *listener, const char *name, va_list ap)
|
||||
{
|
||||
omapi_connection_t *c;
|
||||
omapi_listener_t *l;
|
||||
isc_result_t result;
|
||||
|
||||
REQUIRE(listener != NULL && listener->type == omapi_type_listener);
|
||||
|
||||
|
||||
l = (omapi_listener_t *)listener;
|
||||
|
||||
/*
|
||||
* This function is reached when listener_accept does an
|
||||
* object_signal of "connect" on the listener object. Nothing
|
||||
* need be done here, but the object that originally requested
|
||||
* the listen needs to signalled that a connection was made.
|
||||
* free_connection() signals the listener when one of the connections
|
||||
* it accepted has gone away.
|
||||
*/
|
||||
return (omapi_object_passsignal(listener, name, ap));
|
||||
if (strcmp(name, "disconnect") == 0) {
|
||||
c = va_arg(ap, omapi_connection_t *);
|
||||
|
||||
LOCK(&l->mutex);
|
||||
|
||||
ISC_LIST_UNLINK(l->connections, c, link);
|
||||
|
||||
SIGNAL(&l->waiter);
|
||||
UNLOCK(&l->mutex);
|
||||
|
||||
result = ISC_R_SUCCESS;
|
||||
} else
|
||||
result = omapi_object_passsignal(listener, name, ap);
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
Loading…
Reference in a new issue