Index: freeDiameter/peers.c
===================================================================
--- freeDiameter/peers.c	(revision 14)
+++ freeDiameter/peers.c	(revision 16)
@@ -36,61 +36,11 @@
 #include "fD.h"
 
+/* Global list of peers */
 struct fd_list   fd_g_peers = FD_LIST_INITIALIZER(fd_g_peers);
 pthread_rwlock_t fd_g_peers_rw = PTHREAD_RWLOCK_INITIALIZER;
 
-/* Terminate peer module (destroy all peers) */
-int fd_peer_fini()
-{
-	struct fd_list * li;
-	TRACE_ENTRY();
-	
-	CHECK_FCT_DO(fd_p_expi_fini(), /* continue */);
-	
-	TRACE_DEBUG(INFO, "Sending signal to terminate to all peer connections");
-	
-	CHECK_FCT_DO( pthread_rwlock_rdlock(&fd_g_peers_rw), /* continue */ );
-	/* For each peer in the list, ... */
-	for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
-		struct fd_peer * np = (struct fd_peer *)li;
-		CHECK_FCT_DO( fd_psm_terminate(np), /* continue */ );
-	}
-	CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
-	
-	TODO("Give some time to all PSM, then destroy remaining threads");
-	/* fd_psm_abord(struct fd_peer * peer ) */
-	
-	return 0;
-}
-
-/* Dump the list of peers */
-void fd_peer_dump_list(int details)
-{
-	struct fd_list * li;
-	
-	fd_log_debug("Dumping list of peers :\n");
-	CHECK_FCT_DO( pthread_rwlock_rdlock(&fd_g_peers_rw), /* continue */ );
-	
-	for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
-		struct fd_peer * np = (struct fd_peer *)li;
-		if (np->p_eyec != EYEC_PEER) {
-			fd_log_debug("  Invalid entry @%p !\n", li);
-			continue;
-		}
-		
-		fd_log_debug("   %s\t%s", STATE_STR(np->p_hdr.info.pi_state), np->p_hdr.info.pi_diamid);
-		if (details > INFO) {
-			fd_log_debug("\t(rlm:%s)", np->p_hdr.info.pi_realm);
-			if (np->p_hdr.info.pi_prodname)
-				fd_log_debug("\t['%s' %u]", np->p_hdr.info.pi_prodname, np->p_hdr.info.pi_firmrev);
-			fd_log_debug("\t(from %s)", np->p_dbgorig);
-		}
-		fd_log_debug("\n");
-	}
-	
-	CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
-}
 
 /* Alloc / reinit a peer structure. if *ptr is not NULL, it must already point to a valid struct fd_peer. */
-static int fd_sp_reinit(struct fd_peer ** ptr)
+int fd_peer_alloc(struct fd_peer ** ptr)
 {
 	struct fd_peer *p;
@@ -126,89 +76,4 @@
 }
 
-#define free_null( _v ) 	\
-	if (_v) {		\
-		free(_v);	\
-		(_v) = NULL;	\
-	}
-	
-#define free_list( _l ) 						\
-	while (!FD_IS_LIST_EMPTY(_l)) {					\
-		struct fd_list * __li = ((struct fd_list *)(_l))->next;	\
-		fd_list_unlink(__li);					\
-		free(__li);						\
-	}
-
-/* Destroy a structure once all cleanups have been performed */
-static int fd_sp_destroy(struct fd_peer ** ptr)
-{
-	struct fd_peer *p;
-	void * t;
-	
-	TRACE_ENTRY("%p", ptr);
-	CHECK_PARAMS(ptr);
-	p = *ptr;
-	*ptr = NULL;
-	CHECK_PARAMS(p);
-	
-	CHECK_PARAMS( FD_IS_LIST_EMPTY(&p->p_hdr.chain) );
-	
-	free_null(p->p_hdr.info.pi_diamid); 
-	free_null(p->p_hdr.info.pi_realm); 
-	free_list( &p->p_hdr.info.pi_endpoints );
-	/* Assume the security data is already freed */
-	free_null(p->p_hdr.info.pi_prodname);
-	free_list( &p->p_hdr.info.pi_apps );
-	
-	free_null(p->p_dbgorig);
-	ASSERT(FD_IS_LIST_EMPTY(&p->p_expiry));
-	ASSERT(FD_IS_LIST_EMPTY(&p->p_actives));
-	
-	CHECK_FCT( fd_thr_term(&p->p_psm) );
-	while ( fd_fifo_tryget(p->p_events, &t) == 0 ) {
-		struct fd_event * ev = t;
-		TRACE_DEBUG(FULL, "Found event %d(%p) in queue of peer %p being destroyed", ev->code, ev->data, p);
-		free(ev);
-	}
-	CHECK_FCT( fd_fifo_del(&p->p_events) );
-	
-	CHECK_FCT( fd_thr_term(&p->p_inthr) );
-	while ( fd_fifo_tryget(p->p_recv, &t) == 0 ) {
-		struct msg * m = t;
-		TRACE_DEBUG(FULL, "Found message %p in incoming queue of peer %p being destroyed", m, p);
-		/* We simply destroy, the remote peer will re-send to someone else...*/
-		CHECK_FCT(fd_msg_free(m));
-	}
-	CHECK_FCT( fd_fifo_del(&p->p_recv) );
-	
-	CHECK_FCT( fd_thr_term(&p->p_outthr) );
-	while ( fd_fifo_tryget(p->p_tosend, &t) == 0 ) {
-		struct msg * m = t;
-		TRACE_DEBUG(FULL, "Found message %p in outgoing queue of peer %p being destroyed, requeue", m, p);
-		/* We simply requeue in global, the routing thread will re-handle it. */
-		
-	}
-	CHECK_FCT( fd_fifo_del(&p->p_tosend) );
-	
-	while (!FD_IS_LIST_EMPTY(&p->p_sentreq)) {
-		struct sentreq * sr = (struct sentreq *)(p->p_sentreq.next);
-		fd_list_unlink(&sr->chain);
-		TRACE_DEBUG(FULL, "Found message %p in list of sent requests to peer %p being destroyed, requeue (fallback)", sr->req, p);
-		CHECK_FCT(fd_fifo_post(fd_g_outgoing, &sr->req));
-		free(sr);
-	}
-	
-	if (p->p_cnxctx) {
-		TODO("destroy p->p_cnxctx");
-	}
-	
-	if (p->p_cb)
-		(*p->p_cb)(NULL, p->p_cb_data);
-	
-	free(p);
-	
-	return 0;
-}
-
-
 /* Add a new peer entry */
 int fd_peer_add ( struct peer_info * info, char * orig_dbg, void (*cb)(struct peer_info *, void *), void * cb_data )
@@ -221,5 +86,5 @@
 	
 	/* Create a structure to contain the new peer information */
-	CHECK_FCT( fd_sp_reinit(&p) );
+	CHECK_FCT( fd_peer_alloc(&p) );
 	
 	/* Copy the informations from the parameters received */
@@ -248,7 +113,4 @@
 			fd_list_insert_before(&p->p_hdr.info.pi_endpoints, li);
 		}
-	
-	p->p_hdr.info.pi_sec_module = info->pi_sec_module;
-	memcpy(&p->p_hdr.info.pi_sec_data, &info->pi_sec_data, sizeof(info->pi_sec_data));
 	
 	/* The internal data */
@@ -286,5 +148,5 @@
 	CHECK_POSIX( pthread_rwlock_unlock(&fd_g_peers_rw) );
 	if (ret) {
-		CHECK_FCT( fd_sp_destroy(&p) );
+		CHECK_FCT( fd_peer_free(&p) );
 	} else {
 		CHECK_FCT( fd_psm_begin(p) );
@@ -292,2 +154,218 @@
 	return ret;
 }
+
+
+#define free_null( _v ) 	\
+	if (_v) {		\
+		free(_v);	\
+		(_v) = NULL;	\
+	}
+	
+#define free_list( _l ) 						\
+	while (!FD_IS_LIST_EMPTY(_l)) {					\
+		struct fd_list * __li = ((struct fd_list *)(_l))->next;	\
+		fd_list_unlink(__li);					\
+		free(__li);						\
+	}
+
+/* Destroy a structure once all cleanups have been performed */
+int fd_peer_free(struct fd_peer ** ptr)
+{
+	struct fd_peer *p;
+	void * t;
+	
+	TRACE_ENTRY("%p", ptr);
+	CHECK_PARAMS(ptr);
+	p = *ptr;
+	*ptr = NULL;
+	CHECK_PARAMS(p);
+	
+	CHECK_PARAMS( FD_IS_LIST_EMPTY(&p->p_hdr.chain) );
+	
+	free_null(p->p_hdr.info.pi_diamid); 
+	free_null(p->p_hdr.info.pi_realm); 
+	free_list( &p->p_hdr.info.pi_endpoints );
+	/* Assume the security data is already freed */
+	free_null(p->p_hdr.info.pi_prodname);
+	free_list( &p->p_hdr.info.pi_apps );
+	
+	free_null(p->p_dbgorig);
+	ASSERT(FD_IS_LIST_EMPTY(&p->p_expiry));
+	ASSERT(FD_IS_LIST_EMPTY(&p->p_actives));
+	
+	CHECK_FCT( fd_thr_term(&p->p_psm) );
+	while ( fd_fifo_tryget(p->p_events, &t) == 0 ) {
+		struct fd_event * ev = t;
+		TRACE_DEBUG(FULL, "Found event %d(%p) in queue of peer %p being destroyed", ev->code, ev->data, p);
+		free(ev);
+	}
+	CHECK_FCT( fd_fifo_del(&p->p_events) );
+	
+	CHECK_FCT( fd_thr_term(&p->p_inthr) );
+	while ( fd_fifo_tryget(p->p_recv, &t) == 0 ) {
+		struct msg * m = t;
+		TRACE_DEBUG(FULL, "Found message %p in incoming queue of peer %p being destroyed", m, p);
+		/* We simply destroy, the remote peer will re-send to someone else...*/
+		CHECK_FCT(fd_msg_free(m));
+	}
+	CHECK_FCT( fd_fifo_del(&p->p_recv) );
+	
+	CHECK_FCT( fd_thr_term(&p->p_outthr) );
+	while ( fd_fifo_tryget(p->p_tosend, &t) == 0 ) {
+		struct msg * m = t;
+		TRACE_DEBUG(FULL, "Found message %p in outgoing queue of peer %p being destroyed, requeue", m, p);
+		/* We simply requeue in global, the routing thread will re-handle it. */
+		
+	}
+	CHECK_FCT( fd_fifo_del(&p->p_tosend) );
+	
+	while (!FD_IS_LIST_EMPTY(&p->p_sentreq)) {
+		struct sentreq * sr = (struct sentreq *)(p->p_sentreq.next);
+		fd_list_unlink(&sr->chain);
+		TRACE_DEBUG(FULL, "Found message %p in list of sent requests to peer %p being destroyed, requeue (fallback)", sr->req, p);
+		CHECK_FCT(fd_fifo_post(fd_g_outgoing, &sr->req));
+		free(sr);
+	}
+	
+	if (p->p_cnxctx) {
+		TODO("destroy p->p_cnxctx");
+	}
+	
+	if (p->p_cb)
+		(*p->p_cb)(NULL, p->p_cb_data);
+	
+	free(p);
+	
+	return 0;
+}
+
+/* Terminate peer module (destroy all peers) */
+int fd_peer_fini()
+{
+	struct fd_list * li;
+	struct fd_list purge = FD_LIST_INITIALIZER(purge); /* Store zombie peers here */
+	int list_empty;
+	struct timespec	wait_until, now;
+	
+	TRACE_ENTRY();
+	
+	CHECK_FCT_DO(fd_p_expi_fini(), /* continue */);
+	
+	TRACE_DEBUG(INFO, "Sending terminate signal to all peer connections");
+	
+	CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ );
+	for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
+		struct fd_peer * peer = (struct fd_peer *)li;
+		
+		if (peer->p_hdr.info.pi_state != STATE_ZOMBIE) {
+			CHECK_FCT_DO( fd_psm_terminate(peer), /* continue */ );
+		} else {
+			li = li->prev; /* to avoid breaking the loop */
+			fd_list_unlink(&peer->p_hdr.chain);
+			fd_list_insert_before(&purge, &peer->p_hdr.chain);
+		}
+	}
+	list_empty = FD_IS_LIST_EMPTY(&fd_g_peers);
+	CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
+	
+	if (!list_empty) {
+		CHECK_SYS(  clock_gettime(CLOCK_REALTIME, &now)  );
+		TRACE_DEBUG(INFO, "Waiting for connections shutdown... (%d sec max)", DPR_TIMEOUT);
+		wait_until.tv_sec  = now.tv_sec + DPR_TIMEOUT;
+		wait_until.tv_nsec = now.tv_nsec;
+	}
+	
+	while ((!list_empty) && (TS_IS_INFERIOR(&now, &wait_until))) {
+		
+		/* Allow the PSM(s) to execute */
+		pthread_yield();
+		
+		/* Remove zombie peers */
+		CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ );
+		for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
+			struct fd_peer * peer = (struct fd_peer *)li;
+			if (peer->p_hdr.info.pi_state == STATE_ZOMBIE) {
+				li = li->prev; /* to avoid breaking the loop */
+				fd_list_unlink(&peer->p_hdr.chain);
+				fd_list_insert_before(&purge, &peer->p_hdr.chain);
+			}
+		}
+		list_empty = FD_IS_LIST_EMPTY(&fd_g_peers);
+		CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
+	}
+	
+	if (!list_empty) {
+		TRACE_DEBUG(INFO, "Forcing connections shutdown");
+		CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ );
+		while (!FD_IS_LIST_EMPTY(&fd_g_peers)) {
+			struct fd_peer * peer = (struct fd_peer *)(fd_g_peers.next);
+			fd_psm_abord(peer);
+			fd_list_unlink(&peer->p_hdr.chain);
+			fd_list_insert_before(&purge, &peer->p_hdr.chain);
+		}
+		CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
+	}
+	
+	/* Free memory objects of all peers */
+	while (!FD_IS_LIST_EMPTY(&purge)) {
+		struct fd_peer * peer = (struct fd_peer *)(purge.next);
+		fd_list_unlink(&peer->p_hdr.chain);
+		fd_peer_free(&peer);
+	}
+	
+	return 0;
+}
+
+/* Dump info of one peer */
+void fd_peer_dump(struct fd_peer * peer, int details)
+{
+	if (peer->p_eyec != EYEC_PEER) {
+		fd_log_debug("  Invalid peer @ %p !\n", peer);
+		return;
+	}
+
+	fd_log_debug(">  %s\t%s", STATE_STR(peer->p_hdr.info.pi_state), peer->p_hdr.info.pi_diamid);
+	if (details > INFO) {
+		fd_log_debug("\t(rlm:%s)", peer->p_hdr.info.pi_realm);
+		if (peer->p_hdr.info.pi_prodname)
+			fd_log_debug("\t['%s' %u]", peer->p_hdr.info.pi_prodname, peer->p_hdr.info.pi_firmrev);
+	}
+	fd_log_debug("\n");
+	if (details > FULL) {
+		/* Dump all info */
+		fd_log_debug("\tEntry origin : %s\n", peer->p_dbgorig);
+		fd_log_debug("\tFlags : %s%s%s%s%s - %s%s%s\n", 
+				peer->p_hdr.info.pi_flags.pro3 == PI_P3_DEFAULT ? "" :
+					(peer->p_hdr.info.pi_flags.pro3 == PI_P3_IP ? "IP." : "IPv6."),
+				peer->p_hdr.info.pi_flags.pro4 == PI_P4_DEFAULT ? "" :
+					(peer->p_hdr.info.pi_flags.pro4 == PI_P4_TCP ? "TCP." : "SCTP."),
+				peer->p_hdr.info.pi_flags.alg ? "PrefTCP." : "",
+				peer->p_hdr.info.pi_flags.sec == PI_SEC_DEFAULT ? "" :
+					(peer->p_hdr.info.pi_flags.sec == PI_SEC_NONE ? "IPSec." : "InbandTLS."),
+				peer->p_hdr.info.pi_flags.exp ? "Expire." : "",
+				peer->p_hdr.info.pi_flags.inband & PI_INB_NONE ? "InbandIPsecOK." : "",
+				peer->p_hdr.info.pi_flags.inband & PI_INB_TLS ?  "InbandTLSOK." : "",
+				peer->p_hdr.info.pi_flags.relay ? "Relay (0xffffff)" : "No relay"
+				);
+		fd_log_debug("\tLifetime : %d sec\n", peer->p_hdr.info.pi_lft);
+		
+		TODO("Dump remaining useful information");
+	}
+}
+
+/* Dump the list of peers */
+void fd_peer_dump_list(int details)
+{
+	struct fd_list * li;
+	
+	fd_log_debug("Dumping list of peers :\n");
+	CHECK_FCT_DO( pthread_rwlock_rdlock(&fd_g_peers_rw), /* continue */ );
+	
+	for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
+		struct fd_peer * np = (struct fd_peer *)li;
+		fd_peer_dump(np, details);
+	}
+	
+	CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
+}
+
