diff --git a/server/core/session.c b/server/core/session.c index 09636e69e..a2f687a38 100644 --- a/server/core/session.c +++ b/server/core/session.c @@ -22,12 +22,12 @@ * @verbatim * Revision History * - * Date Who Description - * 17/06/13 Mark Riddoch Initial implementation - * 02/09/13 Massimiliano Pinto Added session refcounter - * 29/05/14 Mark Riddoch Addition of filter mechanism - * 23/08/15 Martin Brampton Tidying; slight improvement in safety - * 17/09/15 Martin Brampton Keep failed session in existence - leave DCBs to close + * Date Who Description + * 17/06/13 Mark Riddoch Initial implementation + * 02/09/13 Massimiliano Pinto Added session refcounter + * 29/05/14 Mark Riddoch Addition of filter mechanism + * 23/08/15 Martin Brampton Tidying; slight improvement in safety + * 17/09/15 Martin Brampton Keep failed session in existence - leave DCBs to close * * @endverbatim */ @@ -49,8 +49,8 @@ /** Global session id; updated safely by holding session_spin */ static size_t session_id; -static SPINLOCK session_spin = SPINLOCK_INIT; -static SESSION *allSessions = NULL; +static SPINLOCK session_spin = SPINLOCK_INIT; +static SESSION *allSessions = NULL; static struct session session_dummy_struct; @@ -64,19 +64,19 @@ static void session_simple_free(SESSION *session, DCB *dcb); * entry point of the router using the router instance of the * service this session is part of. * - * @param service The service this connection was established by - * @param client_dcb The client side DCB - * @return The newly created session or NULL if an error occured + * @param service The service this connection was established by + * @param client_dcb The client side DCB + * @return The newly created session or NULL if an error occured */ SESSION * session_alloc(SERVICE *service, DCB *client_dcb) { - SESSION *session; + SESSION *session; session = (SESSION *)calloc(1, sizeof(SESSION)); ss_info_dassert(session != NULL, "Allocating memory for session failed."); - - if (session == NULL) + + if (session == NULL) { char errbuf[STRERROR_BUFLEN]; MXS_ERROR("Failed to allocate memory for " @@ -84,7 +84,7 @@ session_alloc(SERVICE *service, DCB *client_dcb) errno, strerror_r(errno, errbuf, sizeof(errbuf))); /* Does this possibly need a lock? */ - /* + /* * This is really not the right way to do this. The data in a DCB is * router specific and should be freed by a function in the relevant * router. This would be better achieved by placing a function reference @@ -92,7 +92,7 @@ session_alloc(SERVICE *service, DCB *client_dcb) * at the final destruction of the DCB. However, this piece of code is * only run following a calloc failure, so the system is probably on * the point of crashing anyway. - * + * */ if (client_dcb->data && !DCB_IS_CLONE(client_dcb)) { @@ -128,9 +128,9 @@ session_alloc(SERVICE *service, DCB *client_dcb) * DCBs. Note that this doesn't mean that router is initialized yet! */ session->state = SESSION_STATE_READY; - + /* - * Only create a router session if we are not the listening + * Only create a router session if we are not the listening * DCB or an internal DCB. Creating a router session may create a connection to a * backend server, depending upon the router module implementation * and should be avoided for the listener session @@ -139,20 +139,19 @@ session_alloc(SERVICE *service, DCB *client_dcb) * session, therefore it is important that the session lock is * relinquished before the router call. */ - if (client_dcb->state != DCB_STATE_LISTENING && + if (client_dcb->state != DCB_STATE_LISTENING && client_dcb->dcb_role != DCB_ROLE_INTERNAL) { session->router_session = service->router->newSession(service->router_instance, session); - if (session->router_session == NULL) + if (session->router_session == NULL) { session->state = SESSION_STATE_TO_BE_FREED; - + MXS_ERROR("%lu [%s] Error : Failed to create %s session because router" "could not establish a new router session, see earlier error.", pthread_self(), __func__, service->name); - } /* * Pending filter chain being setup set the head of the chain to @@ -176,17 +175,17 @@ session_alloc(SERVICE *service, DCB *client_dcb) && service->n_filters > 0 && !session_setup_filters(session)) { - session->state = SESSION_STATE_TO_BE_FREED; - MXS_ERROR("Setting up filters failed. " - "Terminating session %s.", - service->name); + session->state = SESSION_STATE_TO_BE_FREED; + MXS_ERROR("Setting up filters failed. " + "Terminating session %s.", + service->name); } } if (SESSION_STATE_TO_BE_FREED != session->state) { session->state = SESSION_STATE_ROUTER_READY; - + if (session->client->user == NULL) { MXS_INFO("Started session [%lu] for %s service ", @@ -213,14 +212,14 @@ session_alloc(SERVICE *service, DCB *client_dcb) } spinlock_acquire(&session_spin); /** Assign a session id and increase, insert session into list */ - session->ses_id = ++session_id; + session->ses_id = ++session_id; session->next = allSessions; allSessions = session; spinlock_release(&session_spin); atomic_add(&service->stats.n_sessions, 1); atomic_add(&service->stats.n_current, 1); CHK_SESSION(session); - + client_dcb->session = session; return SESSION_STATE_TO_BE_FREED == session->state ? NULL : session; } @@ -229,15 +228,15 @@ session_alloc(SERVICE *service, DCB *client_dcb) * Allocate a dummy session so that DCBs can always have sessions. * * Only one dummy session exists, it is statically declared - * - * @param client_dcb The client side DCB - * @return The dummy created session + * + * @param client_dcb The client side DCB + * @return The dummy created session */ SESSION * session_set_dummy(DCB *client_dcb) { - SESSION *session; - + SESSION *session; + session = &session_dummy_struct; #if defined(SS_DEBUG) session->ses_chk_top = CHK_NUM_SESSION; @@ -253,7 +252,7 @@ session_set_dummy(DCB *client_dcb) session->state = SESSION_STATE_DUMMY; session->data = NULL; session->refcount = 1; - session->ses_id = 0; + session->ses_id = 0; session->next = NULL; client_dcb->session = session; @@ -265,7 +264,7 @@ session_set_dummy(DCB *client_dcb) * counter. * Generic logging setting has precedence over session-specific setting. * - * @param ses session + * @param ses session * @param priority syslog priority */ void session_enable_log_priority(SESSION* ses, int priority) @@ -279,7 +278,7 @@ void session_enable_log_priority(SESSION* ses, int priority) * counter. * Generic logging setting has precedence over session-specific setting. * - * @param ses session + * @param ses session * @param priority syslog priority */ void session_disable_log_priority(SESSION* ses, int priority) @@ -294,69 +293,68 @@ void session_disable_log_priority(SESSION* ses, int priority) /** * Link a session to a DCB. * - * @param session The session to link with the dcb - * @param dcb The DCB to be linked - * @return True if the session was sucessfully linked to the DCB + * @param session The session to link with the dcb + * @param dcb The DCB to be linked + * @return True if the session was sucessfully linked to the DCB */ bool session_link_dcb(SESSION *session, DCB *dcb) { - spinlock_acquire(&session->ses_lock); - ss_info_dassert(session->state != SESSION_STATE_FREE, - "If session->state is SESSION_STATE_FREE then this attempt to " - "access freed memory block."); - if (session->state == SESSION_STATE_FREE) - { - spinlock_release(&session->ses_lock); - return false; - } - atomic_add(&session->refcount, 1); - dcb->session = session; - spinlock_release(&session->ses_lock); - return true; + spinlock_acquire(&session->ses_lock); + ss_info_dassert(session->state != SESSION_STATE_FREE, + "If session->state is SESSION_STATE_FREE then this attempt to " + "access freed memory block."); + if (session->state == SESSION_STATE_FREE) + { + spinlock_release(&session->ses_lock); + return false; + } + atomic_add(&session->refcount, 1); + dcb->session = session; + spinlock_release(&session->ses_lock); + return true; } -int session_unlink_dcb( - SESSION* session, - DCB* dcb) +int session_unlink_dcb(SESSION* session, + DCB* dcb) { - int nlink; - - CHK_SESSION(session); - - spinlock_acquire(&session->ses_lock); - ss_dassert(session->refcount > 0); - /*< - * Remove dcb from session's router_client_session. - */ - nlink = atomic_add(&session->refcount, -1); - nlink -= 1; + int nlink; - if (nlink == 0) - { - session->state = SESSION_STATE_TO_BE_FREED; - } + CHK_SESSION(session); - if (dcb != NULL) + spinlock_acquire(&session->ses_lock); + ss_dassert(session->refcount > 0); + /*< + * Remove dcb from session's router_client_session. + */ + nlink = atomic_add(&session->refcount, -1); + nlink -= 1; + + if (nlink == 0) + { + session->state = SESSION_STATE_TO_BE_FREED; + } + + if (dcb != NULL) + { + if (session->client == dcb) { - if (session->client == dcb) - { - session->client = NULL; - } - dcb->session = NULL; + session->client = NULL; } - spinlock_release(&session->ses_lock); - - return nlink; + dcb->session = NULL; + } + spinlock_release(&session->ses_lock); + + return nlink; } /** * Deallocate the specified session, minimal actions during session_alloc - * Since changes to keep new session in existence until all related DCBs + * Since changes to keep new session in existence until all related DCBs * have been destroyed, this function is redundant. Just left until we are * sure of the direction taken. * - * @param session The session to deallocate + * @param session The session to deallocate */ static void session_simple_free(SESSION *session, DCB *dcb) @@ -382,15 +380,14 @@ session_simple_free(SESSION *session, DCB *dcb) } session->state = SESSION_STATE_STOPPING; } - + free(session); } - /** * Deallocate the specified session * - * @param session The session to deallocate + * @param session The session to deallocate */ bool session_free(SESSION *session) @@ -399,137 +396,138 @@ session_free(SESSION *session) { return true; } - CHK_SESSION(session); - - /* - * Remove one reference. If there are no references left, - * free session. - */ - if (atomic_add(&session->refcount, -1) > 1) - { - /* Must be one or more references left */ - return false; - } - session->state = SESSION_STATE_TO_BE_FREED; - - /* First of all remove from the linked list */ - spinlock_acquire(&session_spin); - if (allSessions == session) - { - allSessions = session->next; - } - else - { - SESSION *chksession; - chksession = allSessions; - while (chksession && chksession->next != session) - { - chksession = chksession->next; - } - if (chksession) - { - chksession->next = session->next; - } - } - spinlock_release(&session_spin); - atomic_add(&session->service->stats.n_current, -1); + CHK_SESSION(session); - /** - * If session is not child of some other session, free router_session. - * Otherwise let the parent free it. - */ - if (!session->ses_is_child && session->router_session) - { - session->service->router->freeSession( - session->service->router_instance, - session->router_session); + /* + * Remove one reference. If there are no references left, + * free session. + */ + if (atomic_add(&session->refcount, -1) > 1) + { + /* Must be one or more references left */ + return false; + } + session->state = SESSION_STATE_TO_BE_FREED; + + /* First of all remove from the linked list */ + spinlock_acquire(&session_spin); + if (allSessions == session) + { + allSessions = session->next; + } + else + { + SESSION *chksession; + chksession = allSessions; + while (chksession && chksession->next != session) + { + chksession = chksession->next; } - if (session->n_filters) - { - int i; - for (i = 0; i < session->n_filters; i++) - { - if (session->filters[i].filter) - session->filters[i].filter->obj->closeSession( - session->filters[i].instance, - session->filters[i].session); - } - for (i = 0; i < session->n_filters; i++) - { - if (session->filters[i].filter) - session->filters[i].filter->obj->freeSession( - session->filters[i].instance, - session->filters[i].session); - } - free(session->filters); - } - - MXS_INFO("Stopped %s client session [%lu]", - session->service->name, - session->ses_id); - - /** Disable trace and decrease trace logger counter */ - session_disable_log_priority(session, LOG_INFO); - - /** If session doesn't have parent referencing to it, it can be freed */ - if (!session->ses_is_child) - { - session->state = SESSION_STATE_FREE; - - if (session->data) - { - free(session->data); - } - free(session); - } - return true; + if (chksession) + { + chksession->next = session->next; + } + } + spinlock_release(&session_spin); + atomic_add(&session->service->stats.n_current, -1); + + /** + * If session is not child of some other session, free router_session. + * Otherwise let the parent free it. + */ + if (!session->ses_is_child && session->router_session) + { + session->service->router->freeSession(session->service->router_instance, + session->router_session); + } + if (session->n_filters) + { + int i; + for (i = 0; i < session->n_filters; i++) + { + if (session->filters[i].filter) + { + session->filters[i].filter->obj->closeSession(session->filters[i].instance, + session->filters[i].session); + } + } + for (i = 0; i < session->n_filters; i++) + { + if (session->filters[i].filter) + { + session->filters[i].filter->obj->freeSession(session->filters[i].instance, + session->filters[i].session); + } + } + free(session->filters); + } + + MXS_INFO("Stopped %s client session [%lu]", + session->service->name, + session->ses_id); + + /** Disable trace and decrease trace logger counter */ + session_disable_log_priority(session, LOG_INFO); + + /** If session doesn't have parent referencing to it, it can be freed */ + if (!session->ses_is_child) + { + session->state = SESSION_STATE_FREE; + + if (session->data) + { + free(session->data); + } + free(session); + } + return true; } /** * Check to see if a session is valid, i.e. in the list of all sessions * - * @param session Session to check - * @return 1 if the session is valid otherwise 0 + * @param session Session to check + * @return 1 if the session is valid otherwise 0 */ int session_isvalid(SESSION *session) { -SESSION *ptr; -int rval = 0; + SESSION *ptr; + int rval = 0; - spinlock_acquire(&session_spin); - ptr = allSessions; - while (ptr) - { - if (ptr == session) - { - rval = 1; - break; - } - ptr = ptr->next; - } - spinlock_release(&session_spin); + spinlock_acquire(&session_spin); + ptr = allSessions; + while (ptr) + { + if (ptr == session) + { + rval = 1; + break; + } + ptr = ptr->next; + } + spinlock_release(&session_spin); - return rval; + return rval; } /** * Print details of an individual session * - * @param session Session to print + * @param session Session to print */ void printSession(SESSION *session) { -struct tm result; -char timebuf[40]; + struct tm result; + char timebuf[40]; - printf("Session %p\n", session); - printf("\tState: %s\n", session_state(session->state)); - printf("\tService: %s (%p)\n", session->service->name, session->service); - printf("\tClient DCB: %p\n", session->client); - printf("\tConnected: %s", - asctime_r(localtime_r(&session->stats.connect, &result), timebuf)); + printf("Session %p\n", session); + printf("\tState: %s\n", session_state(session->state)); + printf("\tService: %s (%p)\n", session->service->name, session->service); + printf("\tClient DCB: %p\n", session->client); + printf("\tConnected: %s", + asctime_r(localtime_r(&session->stats.connect, &result), timebuf)); } /** @@ -541,16 +539,16 @@ char timebuf[40]; void printAllSessions() { -SESSION *ptr; + SESSION *ptr; - spinlock_acquire(&session_spin); - ptr = allSessions; - while (ptr) - { - printSession(ptr); - ptr = ptr->next; - } - spinlock_release(&session_spin); + spinlock_acquire(&session_spin); + ptr = allSessions; + while (ptr) + { + printSession(ptr); + ptr = ptr->next; + } + spinlock_release(&session_spin); } @@ -563,56 +561,60 @@ SESSION *ptr; void CheckSessions() { -SESSION *ptr; -int noclients = 0; -int norouter = 0; + SESSION *ptr; + int noclients = 0; + int norouter = 0; - spinlock_acquire(&session_spin); - ptr = allSessions; - while (ptr) - { - if (ptr->state != SESSION_STATE_LISTENER || - ptr->state != SESSION_STATE_LISTENER_STOPPED) - { - if (ptr->client == NULL && ptr->refcount) - { - if (noclients == 0) - { - printf("Sessions without a client DCB.\n"); - printf("==============================\n"); - } - printSession(ptr); - noclients++; - } - } - ptr = ptr->next; - } - spinlock_release(&session_spin); - if (noclients) - printf("%d Sessions have no clients\n", noclients); - spinlock_acquire(&session_spin); - ptr = allSessions; - while (ptr) - { - if (ptr->state != SESSION_STATE_LISTENER || - ptr->state != SESSION_STATE_LISTENER_STOPPED) - { - if (ptr->router_session == NULL && ptr->refcount) - { - if (norouter == 0) - { - printf("Sessions without a router session.\n"); - printf("==================================\n"); - } - printSession(ptr); - norouter++; - } - } - ptr = ptr->next; - } - spinlock_release(&session_spin); - if (norouter) - printf("%d Sessions have no router session\n", norouter); + spinlock_acquire(&session_spin); + ptr = allSessions; + while (ptr) + { + if (ptr->state != SESSION_STATE_LISTENER || + ptr->state != SESSION_STATE_LISTENER_STOPPED) + { + if (ptr->client == NULL && ptr->refcount) + { + if (noclients == 0) + { + printf("Sessions without a client DCB.\n"); + printf("==============================\n"); + } + printSession(ptr); + noclients++; + } + } + ptr = ptr->next; + } + spinlock_release(&session_spin); + if (noclients) + { + printf("%d Sessions have no clients\n", noclients); + } + spinlock_acquire(&session_spin); + ptr = allSessions; + while (ptr) + { + if (ptr->state != SESSION_STATE_LISTENER || + ptr->state != SESSION_STATE_LISTENER_STOPPED) + { + if (ptr->router_session == NULL && ptr->refcount) + { + if (norouter == 0) + { + printf("Sessions without a router session.\n"); + printf("==================================\n"); + } + printSession(ptr); + norouter++; + } + } + ptr = ptr->next; + } + spinlock_release(&session_spin); + if (norouter) + { + printf("%d Sessions have no router session\n", norouter); + } } /** @@ -621,46 +623,45 @@ int norouter = 0; * Designed to be called within a debugger session in order * to display all active sessions within the gateway * - * @param dcb The DCB to print to + * @param dcb The DCB to print to */ void dprintAllSessions(DCB *dcb) { -struct tm result; -char timebuf[40]; -SESSION *ptr; + struct tm result; + char timebuf[40]; + SESSION *ptr; - spinlock_acquire(&session_spin); - ptr = allSessions; - while (ptr) - { + spinlock_acquire(&session_spin); + ptr = allSessions; + while (ptr) + { + dcb_printf(dcb, "Session %d (%p)\n",ptr->ses_id, ptr); + dcb_printf(dcb, "\tState: %s\n", session_state(ptr->state)); + dcb_printf(dcb, "\tService: %s (%p)\n", ptr->service->name, ptr->service); + dcb_printf(dcb, "\tClient DCB: %p\n", ptr->client); - dcb_printf(dcb, "Session %d (%p)\n",ptr->ses_id, ptr); - dcb_printf(dcb, "\tState: %s\n", session_state(ptr->state)); - dcb_printf(dcb, "\tService: %s (%p)\n", ptr->service->name, ptr->service); - dcb_printf(dcb, "\tClient DCB: %p\n", ptr->client); - - if (ptr->client && ptr->client->remote) - { - dcb_printf(dcb, "\tClient Address: %s%s%s\n", + if (ptr->client && ptr->client->remote) + { + dcb_printf(dcb, "\tClient Address: %s%s%s\n", ptr->client->user?ptr->client->user:"", ptr->client->user?"@":"", ptr->client->remote); - } + } - dcb_printf(dcb, "\tConnected: %s", - asctime_r(localtime_r(&ptr->stats.connect, &result), timebuf)); + dcb_printf(dcb, "\tConnected: %s", + asctime_r(localtime_r(&ptr->stats.connect, &result), timebuf)); - if(ptr->client && ptr->client->state == DCB_STATE_POLLING) - { - double idle = (hkheartbeat - ptr->client->last_read); - idle = idle > 0 ? idle/10.0:0; - dcb_printf(dcb, "\tIdle: %.0f seconds\n",idle); - } - - ptr = ptr->next; - } - spinlock_release(&session_spin); + if (ptr->client && ptr->client->state == DCB_STATE_POLLING) + { + double idle = (hkheartbeat - ptr->client->last_read); + idle = idle > 0 ? idle/10.0:0; + dcb_printf(dcb, "\tIdle: %.0f seconds\n",idle); + } + + ptr = ptr->next; + } + spinlock_release(&session_spin); } /** @@ -669,49 +670,47 @@ SESSION *ptr; * Designed to be called within a debugger session in order * to display all active sessions within the gateway * - * @param dcb The DCB to print to - * @param ptr The session to print + * @param dcb The DCB to print to + * @param ptr The session to print */ void dprintSession(DCB *dcb, SESSION *ptr) { -struct tm result; -char buf[30]; -int i; + struct tm result; + char buf[30]; + int i; + dcb_printf(dcb, "Session %d (%p)\n",ptr->ses_id, ptr); + dcb_printf(dcb, "\tState: %s\n", session_state(ptr->state)); + dcb_printf(dcb, "\tService: %s (%p)\n", ptr->service->name, ptr->service); + dcb_printf(dcb, "\tClient DCB: %p\n", ptr->client); + if (ptr->client && ptr->client->remote) + { + double idle = (hkheartbeat - ptr->client->last_read); + idle = idle > 0 ? idle/10.f : 0; + dcb_printf(dcb, "\tClient Address: %s%s%s\n", + ptr->client->user?ptr->client->user:"", + ptr->client->user?"@":"", + ptr->client->remote); + dcb_printf(dcb, "\tConnected: %s\n", + asctime_r(localtime_r(&ptr->stats.connect, &result), buf)); + if (ptr->client->state == DCB_STATE_POLLING) + { + dcb_printf(dcb, "\tIdle: %.0f seconds\n",idle); + } - dcb_printf(dcb, "Session %d (%p)\n",ptr->ses_id, ptr); - dcb_printf(dcb, "\tState: %s\n", session_state(ptr->state)); - dcb_printf(dcb, "\tService: %s (%p)\n", ptr->service->name, ptr->service); - dcb_printf(dcb, "\tClient DCB: %p\n", ptr->client); - if (ptr->client && ptr->client->remote) - { - double idle = (hkheartbeat - ptr->client->last_read); - idle = idle > 0 ? idle/10.f:0; - dcb_printf(dcb, "\tClient Address: %s%s%s\n", - ptr->client->user?ptr->client->user:"", - ptr->client->user?"@":"", - ptr->client->remote); - dcb_printf(dcb, "\tConnected: %s\n", - asctime_r(localtime_r(&ptr->stats.connect, &result), buf)); - if(ptr->client->state == DCB_STATE_POLLING) - { - dcb_printf(dcb, "\tIdle: %.0f seconds\n",idle); - } - - } - if (ptr->n_filters) - { - for (i = 0; i < ptr->n_filters; i++) - { - dcb_printf(dcb, "\tFilter: %s\n", - ptr->filters[i].filter->name); - ptr->filters[i].filter->obj->diagnostics( - ptr->filters[i].instance, - ptr->filters[i].session, - dcb); - } - } + } + if (ptr->n_filters) + { + for (i = 0; i < ptr->n_filters; i++) + { + dcb_printf(dcb, "\tFilter: %s\n", + ptr->filters[i].filter->name); + ptr->filters[i].filter->obj->diagnostics(ptr->filters[i].instance, + ptr->filters[i].session, + dcb); + } + } } /** @@ -720,87 +719,90 @@ int i; * Designed to be called within a debugger session in order * to display all active sessions within the gateway * - * @param dcb The DCB to print to + * @param dcb The DCB to print to */ void dListSessions(DCB *dcb) { -SESSION *ptr; + SESSION *ptr; - spinlock_acquire(&session_spin); - ptr = allSessions; - if (ptr) - { - dcb_printf(dcb, "Sessions.\n"); - dcb_printf(dcb, "-----------------+-----------------+----------------+--------------------------\n"); - dcb_printf(dcb, "Session | Client | Service | State\n"); - dcb_printf(dcb, "-----------------+-----------------+----------------+--------------------------\n"); - } - while (ptr) - { - dcb_printf(dcb, "%-16p | %-15s | %-14s | %s\n", ptr, - ((ptr->client && ptr->client->remote) - ? ptr->client->remote : ""), - (ptr->service && ptr->service->name ? ptr->service->name - : ""), - session_state(ptr->state)); - ptr = ptr->next; - } - if (allSessions) - dcb_printf(dcb, "-----------------+-----------------+----------------+--------------------------\n\n"); - spinlock_release(&session_spin); + spinlock_acquire(&session_spin); + ptr = allSessions; + if (ptr) + { + dcb_printf(dcb, "Sessions.\n"); + dcb_printf(dcb, "-----------------+-----------------+----------------+--------------------------\n"); + dcb_printf(dcb, "Session | Client | Service | State\n"); + dcb_printf(dcb, "-----------------+-----------------+----------------+--------------------------\n"); + } + while (ptr) + { + dcb_printf(dcb, "%-16p | %-15s | %-14s | %s\n", ptr, + ((ptr->client && ptr->client->remote) + ? ptr->client->remote : ""), + (ptr->service && ptr->service->name ? ptr->service->name + : ""), + session_state(ptr->state)); + ptr = ptr->next; + } + if (allSessions) + { + dcb_printf(dcb, + "-----------------+-----------------+----------------+--------------------------\n\n"); + } + spinlock_release(&session_spin); } /** * Convert a session state to a string representation * - * @param state The session state + * @param state The session state * @return A string representation of the session state */ char * session_state(int state) { - switch (state) - { - case SESSION_STATE_ALLOC: - return "Session Allocated"; - case SESSION_STATE_DUMMY: - return "Dummy Session"; - case SESSION_STATE_READY: - return "Session Ready"; - case SESSION_STATE_ROUTER_READY: - return "Session ready for routing"; - case SESSION_STATE_LISTENER: - return "Listener Session"; - case SESSION_STATE_LISTENER_STOPPED: - return "Stopped Listener Session"; + switch (state) + { + case SESSION_STATE_ALLOC: + return "Session Allocated"; + case SESSION_STATE_DUMMY: + return "Dummy Session"; + case SESSION_STATE_READY: + return "Session Ready"; + case SESSION_STATE_ROUTER_READY: + return "Session ready for routing"; + case SESSION_STATE_LISTENER: + return "Listener Session"; + case SESSION_STATE_LISTENER_STOPPED: + return "Stopped Listener Session"; #ifdef SS_DEBUG - case SESSION_STATE_STOPPING: - return "Stopping session"; - case SESSION_STATE_TO_BE_FREED: - return "Session to be freed"; - case SESSION_STATE_FREE: - return "Freed session"; - + case SESSION_STATE_STOPPING: + return "Stopping session"; + case SESSION_STATE_TO_BE_FREED: + return "Session to be freed"; + case SESSION_STATE_FREE: + return "Freed session"; #endif - default: - return "Invalid State"; - } + default: + return "Invalid State"; + } } -SESSION* get_session_by_router_ses( - void* rses) +SESSION* get_session_by_router_ses(void* rses) { - SESSION* ses = allSessions; - - while (ses->router_session != rses && ses->next != NULL) - ses = ses->next; - - if (ses->router_session != rses) - { - ses = NULL; - } - return ses; + SESSION* ses = allSessions; + + while (ses->router_session != rses && ses->next != NULL) + { + ses = ses->next; + } + + if (ses->router_session != rses) + { + ses = NULL; + } + return ses; } @@ -813,128 +815,128 @@ SESSION* get_session_by_router_ses( * this head becomes the destination for the filter. The newly created * filter becomes the new head of the filter chain. * - * @param session The session that requires the chain - * @return 0 if filter creation fails + * @param session The session that requires the chain + * @return 0 if filter creation fails */ static int session_setup_filters(SESSION *session) { -SERVICE *service = session->service; -DOWNSTREAM *head; -UPSTREAM *tail; -int i; + SERVICE *service = session->service; + DOWNSTREAM *head; + UPSTREAM *tail; + int i; - if ((session->filters = calloc(service->n_filters, - sizeof(SESSION_FILTER))) == NULL) - { - MXS_ERROR("Insufficient memory to allocate session filter " - "tracking.\n"); + if ((session->filters = calloc(service->n_filters, + sizeof(SESSION_FILTER))) == NULL) + { + MXS_ERROR("Insufficient memory to allocate session filter " + "tracking.\n"); + return 0; + } + session->n_filters = service->n_filters; + for (i = service->n_filters - 1; i >= 0; i--) + { + if (service->filters[i] == NULL) + { + MXS_ERROR("Service '%s' contians an unresolved filter.", service->name); return 0; - } - session->n_filters = service->n_filters; - for (i = service->n_filters - 1; i >= 0; i--) - { - if (service->filters[i] == NULL) - { - MXS_ERROR("Service '%s' contians an unresolved filter.", service->name); - return 0; - } - if ((head = filterApply(service->filters[i], session, - &session->head)) == NULL) - { - MXS_ERROR("Failed to create filter '%s' for " - "service '%s'.\n", - service->filters[i]->name, - service->name); - return 0; - } - session->filters[i].filter = service->filters[i]; - session->filters[i].session = head->session; - session->filters[i].instance = head->instance; - session->head = *head; - free(head); - } + } + if ((head = filterApply(service->filters[i], session, + &session->head)) == NULL) + { + MXS_ERROR("Failed to create filter '%s' for " + "service '%s'.\n", + service->filters[i]->name, + service->name); + return 0; + } + session->filters[i].filter = service->filters[i]; + session->filters[i].session = head->session; + session->filters[i].instance = head->instance; + session->head = *head; + free(head); + } - for (i = 0; i < service->n_filters; i++) - { - if ((tail = filterUpstream(service->filters[i], - session->filters[i].session, - &session->tail)) == NULL) - { - MXS_ERROR("Failed to create filter '%s' for service '%s'.", - service->filters[i]->name, - service->name); - return 0; - } + for (i = 0; i < service->n_filters; i++) + { + if ((tail = filterUpstream(service->filters[i], + session->filters[i].session, + &session->tail)) == NULL) + { + MXS_ERROR("Failed to create filter '%s' for service '%s'.", + service->filters[i]->name, + service->name); + return 0; + } - /* - * filterUpstream may simply return the 3 parameter if - * the filter has no upstream entry point. So no need - * to copy the contents or free tail in this case. - */ - if (tail != &session->tail) - { - session->tail = *tail; - free(tail); - } - } + /* + * filterUpstream may simply return the 3 parameter if + * the filter has no upstream entry point. So no need + * to copy the contents or free tail in this case. + */ + if (tail != &session->tail) + { + session->tail = *tail; + free(tail); + } + } - return 1; + return 1; } /** * Entry point for the final element int he upstream filter, i.e. the writing * of the data to the client. * - * @param instance The "instance" data - * @param session The session - * @param data The buffer chain to write + * @param instance The "instance" data + * @param session The session + * @param data The buffer chain to write */ int session_reply(void *instance, void *session, GWBUF *data) { -SESSION *the_session = (SESSION *)session; + SESSION *the_session = (SESSION *)session; - return the_session->client->func.write(the_session->client, data); + return the_session->client->func.write(the_session->client, data); } /** * Return the client connection address or name * - * @param session The session whose client address to return + * @param session The session whose client address to return */ char * session_get_remote(SESSION *session) { - if (session && session->client) - return session->client->remote; - return NULL; + if (session && session->client) + { + return session->client->remote; + } + return NULL; } -bool session_route_query ( - SESSION* ses, - GWBUF* buf) +bool session_route_query(SESSION* ses, GWBUF* buf) { - bool succp; - - if (ses->head.routeQuery == NULL || - ses->head.instance == NULL || - ses->head.session == NULL) - { - succp = false; - goto return_succp; - } + bool succp; - if (ses->head.routeQuery(ses->head.instance, ses->head.session, buf) == 1) - { - succp = true; - } - else - { - succp = false; - } + if (ses->head.routeQuery == NULL || + ses->head.instance == NULL || + ses->head.session == NULL) + { + succp = false; + goto return_succp; + } + + if (ses->head.routeQuery(ses->head.instance, ses->head.session, buf) == 1) + { + succp = true; + } + else + { + succp = false; + } return_succp: - return succp; + return succp; } @@ -942,13 +944,13 @@ return_succp: * Return the username of the user connected to the client side of the * session. * - * @param session The session pointer. - * @return The user name or NULL if it can not be determined. + * @param session The session pointer. + * @return The user name or NULL if it can not be determined. */ char * session_getUser(SESSION *session) { - return (session && session->client) ? session->client->user : NULL; + return (session && session->client) ? session->client->user : NULL; } /** * Return the pointer to the list of all sessions. @@ -956,12 +958,12 @@ session_getUser(SESSION *session) */ SESSION *get_all_sessions() { - return allSessions; + return allSessions; } /** * Close sessions that have been idle for too long. - * + * * If the time since a session last sent data is grater than the set value in the * service, it is disconnected. The default value for the timeout for a service is 0. * This means that connections are never timed out. @@ -970,96 +972,96 @@ SESSION *get_all_sessions() void session_close_timeouts(void* data) { SESSION* ses; - + spinlock_acquire(&session_spin); ses = get_all_sessions(); spinlock_release(&session_spin); - - while(ses) + + while (ses) { - if(ses->client && ses->client->state == DCB_STATE_POLLING && - ses->service->conn_timeout > 0 && - hkheartbeat - ses->client->last_read > ses->service->conn_timeout * 10) - { - dcb_close(ses->client); - } - - spinlock_acquire(&session_spin); - ses = ses->next; - spinlock_release(&session_spin); - + if (ses->client && ses->client->state == DCB_STATE_POLLING && + ses->service->conn_timeout > 0 && + hkheartbeat - ses->client->last_read > ses->service->conn_timeout * 10) + { + dcb_close(ses->client); + } + + spinlock_acquire(&session_spin); + ses = ses->next; + spinlock_release(&session_spin); } } /** * Callback structure for the session list extraction */ -typedef struct { - int index; - SESSIONLISTFILTER filter; +typedef struct +{ + int index; + SESSIONLISTFILTER filter; } SESSIONFILTER; /** * Provide a row to the result set that defines the set of sessions * - * @param set The result set - * @param data The index of the row to send + * @param set The result set + * @param data The index of the row to send * @return The next row or NULL */ static RESULT_ROW * sessionRowCallback(RESULTSET *set, void *data) { -SESSIONFILTER *cbdata = (SESSIONFILTER *)data; -int i = 0; -char buf[20]; -RESULT_ROW *row; -SESSION *ptr; + SESSIONFILTER *cbdata = (SESSIONFILTER *)data; + int i = 0; + char buf[20]; + RESULT_ROW *row; + SESSION *ptr; - spinlock_acquire(&session_spin); - ptr = allSessions; - /* Skip to the first non-listener if not showing listeners */ - while (ptr && cbdata->filter == SESSION_LIST_CONNECTION && - ptr->state == SESSION_STATE_LISTENER) - { - ptr = ptr->next; - } - while (i < cbdata->index && ptr) - { - if (cbdata->filter == SESSION_LIST_CONNECTION && - ptr->state != SESSION_STATE_LISTENER) - { - i++; - } - else if (cbdata->filter == SESSION_LIST_ALL) - { - i++; - } - ptr = ptr->next; - } - /* Skip to the next non-listener if not showing listeners */ - while (ptr && cbdata->filter == SESSION_LIST_CONNECTION && - ptr->state == SESSION_STATE_LISTENER) - { - ptr = ptr->next; - } - if (ptr == NULL) - { - spinlock_release(&session_spin); - free(data); - return NULL; - } - cbdata->index++; - row = resultset_make_row(set); - snprintf(buf,19, "%p", ptr); - buf[19] = '\0'; - resultset_row_set(row, 0, buf); - resultset_row_set(row, 1, ((ptr->client && ptr->client->remote) - ? ptr->client->remote : "")); - resultset_row_set(row, 2, (ptr->service && ptr->service->name - ? ptr->service->name : "")); - resultset_row_set(row, 3, session_state(ptr->state)); - spinlock_release(&session_spin); - return row; + spinlock_acquire(&session_spin); + ptr = allSessions; + /* Skip to the first non-listener if not showing listeners */ + while (ptr && cbdata->filter == SESSION_LIST_CONNECTION && + ptr->state == SESSION_STATE_LISTENER) + { + ptr = ptr->next; + } + while (i < cbdata->index && ptr) + { + if (cbdata->filter == SESSION_LIST_CONNECTION && + ptr->state != SESSION_STATE_LISTENER) + { + i++; + } + else if (cbdata->filter == SESSION_LIST_ALL) + { + i++; + } + ptr = ptr->next; + } + /* Skip to the next non-listener if not showing listeners */ + while (ptr && cbdata->filter == SESSION_LIST_CONNECTION && + ptr->state == SESSION_STATE_LISTENER) + { + ptr = ptr->next; + } + if (ptr == NULL) + { + spinlock_release(&session_spin); + free(data); + return NULL; + } + cbdata->index++; + row = resultset_make_row(set); + snprintf(buf,19, "%p", ptr); + buf[19] = '\0'; + resultset_row_set(row, 0, buf); + resultset_row_set(row, 1, ((ptr->client && ptr->client->remote) + ? ptr->client->remote : "")); + resultset_row_set(row, 2, (ptr->service && ptr->service->name + ? ptr->service->name : "")); + resultset_row_set(row, 3, session_state(ptr->state)); + spinlock_release(&session_spin); + return row; } /** @@ -1070,22 +1072,24 @@ SESSION *ptr; RESULTSET * sessionGetList(SESSIONLISTFILTER filter) { -RESULTSET *set; -SESSIONFILTER *data; + RESULTSET *set; + SESSIONFILTER *data; - if ((data = (SESSIONFILTER *)malloc(sizeof(SESSIONFILTER))) == NULL) - return NULL; - data->index = 0; - data->filter = filter; - if ((set = resultset_create(sessionRowCallback, data)) == NULL) - { - free(data); - return NULL; - } - resultset_add_column(set, "Session", 16, COL_TYPE_VARCHAR); - resultset_add_column(set, "Client", 15, COL_TYPE_VARCHAR); - resultset_add_column(set, "Service", 15, COL_TYPE_VARCHAR); - resultset_add_column(set, "State", 15, COL_TYPE_VARCHAR); + if ((data = (SESSIONFILTER *)malloc(sizeof(SESSIONFILTER))) == NULL) + { + return NULL; + } + data->index = 0; + data->filter = filter; + if ((set = resultset_create(sessionRowCallback, data)) == NULL) + { + free(data); + return NULL; + } + resultset_add_column(set, "Session", 16, COL_TYPE_VARCHAR); + resultset_add_column(set, "Client", 15, COL_TYPE_VARCHAR); + resultset_add_column(set, "Service", 15, COL_TYPE_VARCHAR); + resultset_add_column(set, "State", 15, COL_TYPE_VARCHAR); - return set; + return set; } diff --git a/server/include/session.h b/server/include/session.h index e0d4c725e..45bb91723 100644 --- a/server/include/session.h +++ b/server/include/session.h @@ -24,15 +24,15 @@ * @verbatim * Revision History * - * Date Who Description - * 01-06-2013 Mark Riddoch Initial implementation - * 14-06-2013 Massimiliano Pinto Added void *data to session - * for session specific data - * 01-07-2013 Massimiliano Pinto Removed backends pointer - * from struct session - * 02-09-2013 Massimiliano Pinto Added session ref counter - * 29-05-2014 Mark Riddoch Support for filter mechanism - * added + * Date Who Description + * 01-06-2013 Mark Riddoch Initial implementation + * 14-06-2013 Massimiliano Pinto Added void *data to session + * for session specific data + * 01-07-2013 Massimiliano Pinto Removed backends pointer + * from struct session + * 02-09-2013 Massimiliano Pinto Added session ref counter + * 29-05-2014 Mark Riddoch Support for filter mechanism + * added * 20-02-2015 Markus Mäkelä Added session timeouts * * @endverbatim @@ -52,18 +52,20 @@ struct filter_def; /** * The session statistics structure */ -typedef struct { - time_t connect; /**< Time when the session was started */ +typedef struct +{ + time_t connect; /**< Time when the session was started */ } SESSION_STATS; -typedef enum { +typedef enum +{ SESSION_STATE_ALLOC, /*< for all sessions */ SESSION_STATE_READY, /*< for router session */ SESSION_STATE_ROUTER_READY, /*< for router session */ SESSION_STATE_STOPPING, /*< session and router are being closed */ SESSION_STATE_LISTENER, /*< for listener session */ SESSION_STATE_LISTENER_STOPPED, /*< for listener session */ - SESSION_STATE_TO_BE_FREED, /*< ready to be freed as soon as there are no references */ + SESSION_STATE_TO_BE_FREED, /*< ready to be freed as soon as there are no references */ SESSION_STATE_FREE, /*< for all sessions */ SESSION_STATE_DUMMY /*< dummy session for consistency */ } session_state_t; @@ -72,42 +74,43 @@ typedef enum { * The downstream element in the filter chain. This may refer to * another filter or to a router. */ -typedef struct { - void *instance; - void *session; - int (*routeQuery)(void *instance, void *session, - GWBUF *request); +typedef struct +{ + void *instance; + void *session; + int (*routeQuery)(void *instance, void *session, GWBUF *request); } DOWNSTREAM; /** * The upstream element in the filter chain. This may refer to * another filter or to the protocol implementation. */ -typedef struct { - void *instance; - void *session; - int (*clientReply)(void *instance, - void *session, GWBUF *response); - int (*error)(void *instance, void *session, void *); +typedef struct +{ + void *instance; + void *session; + int (*clientReply)(void *instance, void *session, GWBUF *response); + int (*error)(void *instance, void *session, void *); } UPSTREAM; /** * Structure used to track the filter instances and sessions of the filters * that are in use within a session. */ -typedef struct { - struct filter_def - *filter; - void *instance; - void *session; +typedef struct +{ + struct filter_def *filter; + void *instance; + void *session; } SESSION_FILTER; /** * Filter type for the sessionGetList call */ -typedef enum { - SESSION_LIST_ALL, - SESSION_LIST_CONNECTION +typedef enum +{ + SESSION_LIST_ALL, + SESSION_LIST_CONNECTION } SESSIONLISTFILTER; /** @@ -117,70 +120,71 @@ typedef enum { * to the database, it links the descriptors, routing implementation * and originating service together for the client session. */ -typedef struct session { +typedef struct session +{ #if defined(SS_DEBUG) - skygw_chk_t ses_chk_top; + skygw_chk_t ses_chk_top; #endif - SPINLOCK ses_lock; - session_state_t state; /*< Current descriptor state */ - size_t ses_id; /*< Unique session identifier */ - int enabled_log_priorities; /*< Bitfield of enabled syslog priorities */ - struct dcb *client; /*< The client connection */ - void *data; /*< The session data */ - void *router_session; /*< The router instance data */ - SESSION_STATS stats; /*< Session statistics */ - struct service *service; /*< The service this session is using */ - int n_filters; /*< Number of filter sessions */ - SESSION_FILTER *filters; /*< The filters in use within this session */ - DOWNSTREAM head; /*< Head of the filter chain */ - UPSTREAM tail; /*< The tail of the filter chain */ - struct session *next; /*< Linked list of all sessions */ - int refcount; /*< Reference count on the session */ - bool ses_is_child; /*< this is a child session */ + SPINLOCK ses_lock; + session_state_t state; /*< Current descriptor state */ + size_t ses_id; /*< Unique session identifier */ + int enabled_log_priorities; /*< Bitfield of enabled syslog priorities */ + struct dcb *client; /*< The client connection */ + void *data; /*< The session data */ + void *router_session; /*< The router instance data */ + SESSION_STATS stats; /*< Session statistics */ + struct service *service; /*< The service this session is using */ + int n_filters; /*< Number of filter sessions */ + SESSION_FILTER *filters; /*< The filters in use within this session */ + DOWNSTREAM head; /*< Head of the filter chain */ + UPSTREAM tail; /*< The tail of the filter chain */ + struct session *next; /*< Linked list of all sessions */ + int refcount; /*< Reference count on the session */ + bool ses_is_child; /*< this is a child session */ #if defined(SS_DEBUG) - skygw_chk_t ses_chk_tail; + skygw_chk_t ses_chk_tail; #endif } SESSION; -#define SESSION_PROTOCOL(x, type) DCB_PROTOCOL((x)->client, type) +#define SESSION_PROTOCOL(x, type) DCB_PROTOCOL((x)->client, type) /** * A convenience macro that can be used by the protocol modules to route * the incoming data to the first element in the pipeline of filters and * routers. */ -#define SESSION_ROUTE_QUERY(sess, buf) \ - ((sess)->head.routeQuery)((sess)->head.instance, \ - (sess)->head.session, (buf)) +#define SESSION_ROUTE_QUERY(sess, buf) \ + ((sess)->head.routeQuery)((sess)->head.instance, \ + (sess)->head.session, (buf)) /** * A convenience macro that can be used by the router modules to route * the replies to the first element in the pipeline of filters and * the protocol. */ -#define SESSION_ROUTE_REPLY(sess, buf) \ - ((sess)->tail.clientReply)((sess)->tail.instance, \ - (sess)->tail.session, (buf)) +#define SESSION_ROUTE_REPLY(sess, buf) \ + ((sess)->tail.clientReply)((sess)->tail.instance, \ + (sess)->tail.session, (buf)) SESSION *get_all_sessions(); -SESSION *session_alloc(struct service *, struct dcb *); -SESSION *session_set_dummy(struct dcb *); -bool session_free(SESSION *); -int session_isvalid(SESSION *); -int session_reply(void *inst, void *session, GWBUF *data); -char *session_get_remote(SESSION *); -char *session_getUser(SESSION *); -void printAllSessions(); -void printSession(SESSION *); -void dprintAllSessions(struct dcb *); -void dprintSession(struct dcb *, SESSION *); -void dListSessions(struct dcb *); -char *session_state(int); -bool session_link_dcb(SESSION *, struct dcb *); -int session_unlink_dcb(SESSION*, DCB*); +SESSION *session_alloc(struct service *, struct dcb *); +SESSION *session_set_dummy(struct dcb *); +bool session_free(SESSION *); +int session_isvalid(SESSION *); +int session_reply(void *inst, void *session, GWBUF *data); +char *session_get_remote(SESSION *); +char *session_getUser(SESSION *); +void printAllSessions(); +void printSession(SESSION *); +void dprintAllSessions(struct dcb *); +void dprintSession(struct dcb *, SESSION *); +void dListSessions(struct dcb *); +char *session_state(int); +bool session_link_dcb(SESSION *, struct dcb *); +int session_unlink_dcb(SESSION*, DCB*); SESSION* get_session_by_router_ses(void* rses); void session_enable_log_priority(SESSION* ses, int priority); void session_disable_log_priority(SESSION* ses, int priority); void session_close_timeouts(void* data); -RESULTSET *sessionGetList(SESSIONLISTFILTER); +RESULTSET *sessionGetList(SESSIONLISTFILTER); #endif