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; if (nlink == 0) { session->state = SESSION_STATE_TO_BE_FREED; } if (dcb != NULL) { if (session->client_dcb == dcb) { session->client_dcb = NULL; } dcb->session = NULL; } spinlock_release(&session->ses_lock); return nlink; }
/** * Close a session with the filter, this is the mechanism * by which a filter may cleanup data structure etc. * In the case of the tee filter we need to close down the * "branch" session. * * @param instance The filter instance data * @param session The session being closed */ static void closeSession(FILTER *instance, void *session) { TEE_SESSION *my_session = (TEE_SESSION *) session; ROUTER_OBJECT *router; void *router_instance, *rsession; SESSION *bsession; #ifdef SS_DEBUG MXS_INFO("Tee close: %d", atomic_add(&debug_seq, 1)); #endif if (my_session->active) { if ((bsession = my_session->branch_session) != NULL) { CHK_SESSION(bsession); spinlock_acquire(&bsession->ses_lock); if (bsession->state != SESSION_STATE_STOPPING) { bsession->state = SESSION_STATE_STOPPING; } router = bsession->service->router; router_instance = bsession->service->router_instance; rsession = bsession->router_session; spinlock_release(&bsession->ses_lock); /** Close router session and all its connections */ router->closeSession(router_instance, rsession); } /* No need to free the session, this is done as * a side effect of closing the client DCB of the * session. */ if (my_session->waiting[PARENT]) { if (my_session->command != 0x01 && my_session->client_dcb && my_session->client_dcb->state == DCB_STATE_POLLING) { MXS_INFO("Tee session closed mid-query."); GWBUF* errbuf = modutil_create_mysql_err_msg(1, 0, 1, "00000", "Session closed."); my_session->client_dcb->func.write(my_session->client_dcb, errbuf); } } my_session->active = 0; } }
/** * Allocate a new session for a new client of the specified service. * * Create the link to the router session by calling the newSession * 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 */ SESSION * session_alloc(SERVICE *service, DCB *client_dcb) { SESSION *session; session = (SESSION *)calloc(1, sizeof(SESSION)); ss_info_dassert(session != NULL, "Allocating memory for session failed."); if (session == NULL) { char errbuf[STRERROR_BUFLEN]; MXS_ERROR("Failed to allocate memory for " "session object due error %d, %s.", errno, strerror_r(errno, errbuf, sizeof(errbuf))); return NULL; } #if defined(SS_DEBUG) session->ses_chk_top = CHK_NUM_SESSION; session->ses_chk_tail = CHK_NUM_SESSION; #endif session->ses_is_child = (bool) DCB_IS_CLONE(client_dcb); spinlock_init(&session->ses_lock); session->service = service; session->client_dcb = client_dcb; session->n_filters = 0; memset(&session->stats, 0, sizeof(SESSION_STATS)); session->stats.connect = time(0); session->state = SESSION_STATE_ALLOC; /*< * Associate the session to the client DCB and set the reference count on * the session to indicate that there is a single reference to the * session. There is no need to protect this or use atomic add as the * session has not been made available to the other threads at this * point. */ session->refcount = 1; /*< * This indicates that session is ready to be shared with backend * 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 * 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 * * Router session creation may create other DCBs that link to the * session, therefore it is important that the session lock is * relinquished before the router call. */ 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) { session->state = SESSION_STATE_TO_BE_FREED; MXS_ERROR("Failed to create new router session for service '%s'. " "See previous errors for more details.", service->name); } /* * Pending filter chain being setup set the head of the chain to * be the router. As filters are inserted the current head will * be pushed to the filter and the head updated. * * NB This dictates that filters are created starting at the end * of the chain nearest the router working back to the client * protocol end of the chain. */ session->head.instance = service->router_instance; session->head.session = session->router_session; session->head.routeQuery = (void *)(service->router->routeQuery); session->tail.instance = session; session->tail.session = session; session->tail.clientReply = session_reply; if (SESSION_STATE_TO_BE_FREED != session->state && 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); } } if (SESSION_STATE_TO_BE_FREED != session->state) { session->state = SESSION_STATE_ROUTER_READY; if (session->client_dcb->user == NULL) { MXS_INFO("Started session [%lu] for %s service ", session->ses_id, service->name); } else { MXS_INFO("Started %s client session [%lu] for '%s' from %s", service->name, session->ses_id, session->client_dcb->user, session->client_dcb->remote); } } else { MXS_INFO("Start %s client session [%lu] for '%s' from %s failed, will be " "closed as soon as all related DCBs have been closed.", service->name, session->ses_id, session->client_dcb->user, session->client_dcb->remote); } spinlock_acquire(&session_spin); /** Assign a session id and increase, insert session into list */ 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; }
/** * Deallocate the specified session * * @param session The session to deallocate */ bool session_free(SESSION *session) { if (session && SESSION_STATE_DUMMY == session->state) { 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); /*** * */ if (session->client_dcb) { if (!DCB_IS_CLONE(session->client_dcb)) { mysql_auth_free_client_data(session->client_dcb); } dcb_free_all_memory(session->client_dcb); } /** * 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; free(session); } return true; }
/** * Allocate a new session for a new client of the specified service. * * Create the link to the router session by calling the newSession * 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 */ SESSION * session_alloc(SERVICE *service, DCB *client_dcb) { SESSION *session; session = (SESSION *)calloc(1, sizeof(SESSION)); ss_info_dassert(session != NULL, "Allocating memory for session failed."); if (session == NULL) { int eno = errno; errno = 0; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Failed to allocate memory for " "session object due error %d, %s.", eno, strerror(eno)))); goto return_session; } #if defined(SS_DEBUG) session->ses_chk_top = CHK_NUM_SESSION; session->ses_chk_tail = CHK_NUM_SESSION; #endif spinlock_init(&session->ses_lock); /*< * Prevent backend threads from accessing before session is completely * initialized. */ spinlock_acquire(&session->ses_lock); session->service = service; session->client = client_dcb; session->n_filters = 0; memset(&session->stats, 0, sizeof(SESSION_STATS)); session->stats.connect = time(0); session->state = SESSION_STATE_ALLOC; /*< * Associate the session to the client DCB and set the reference count on * the session to indicate that there is a single reference to the * session. There is no need to protect this or use atomic add as the * session has not been made available to the other threads at this * point. */ session->data = client_dcb->data; client_dcb->session = session; session->refcount = 1; /*< * This indicates that session is ready to be shared with backend * DCBs. Note that this doesn't mean that router is initialized yet! */ session->state = SESSION_STATE_READY; /*< Release session lock */ spinlock_release(&session->ses_lock); /* * 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 * * Router session creation may create other DCBs that link to the * session, therefore it is important that the session lock is * relinquished beforethe router call. */ 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) { /** * Inform other threads that session is closing. */ session->state = SESSION_STATE_STOPPING; /*< * Decrease refcount, set dcb's session pointer NULL * and set session pointer to NULL. */ session_free(session); client_dcb->session = NULL; session = NULL; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Failed to create %s session.", service->name))); goto return_session; } /* * Pending filter chain being setup set the head of the chain to * be the router. As filters are inserted the current head will * be pushed to the filter and the head updated. * * NB This dictates that filters are created starting at the end * of the chain nearest the router working back to the client * protocol end of the chain. */ session->head.instance = service->router_instance; session->head.session = session->router_session; session->head.routeQuery = (void *)(service->router->routeQuery); session->tail.instance = session; session->tail.session = session; session->tail.clientReply = session_reply; if (service->n_filters > 0) { if (!session_setup_filters(session)) { /** * Inform other threads that session is closing. */ session->state = SESSION_STATE_STOPPING; /*< * Decrease refcount, set dcb's session pointer NULL * and set session pointer to NULL. */ session_free(session); client_dcb->session = NULL; session = NULL; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Failed to create %s session.", service->name))); goto return_session; } } } spinlock_acquire(&session_spin); if (session->state != SESSION_STATE_READY) { session_free(session); client_dcb->session = NULL; session = NULL; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Failed to create %s session.", service->name))); spinlock_release(&session_spin); } else { session->state = SESSION_STATE_ROUTER_READY; 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); } return_session: return session; }
/** * Deallocate the specified session * * @param session The session to deallocate */ bool session_free( SESSION *session) { bool succp = false; SESSION *ptr; int nlink; int i; CHK_SESSION(session); /*< * Remove one reference. If there are no references left, * free session. */ nlink = session_unlink_dcb(session, NULL); if (nlink != 0) { ss_dassert(nlink > 0); goto return_succp; } /* First of all remove from the linked list */ spinlock_acquire(&session_spin); if (allSessions == session) { allSessions = session->next; } else { ptr = allSessions; while (ptr && ptr->next != session) { ptr = ptr->next; } if (ptr) ptr->next = session->next; } spinlock_release(&session_spin); atomic_add(&session->service->stats.n_current, -1); /* Free router_session and session */ if (session->router_session) { session->service->router->freeSession( session->service->router_instance, session->router_session); } if (session->n_filters) { for (i = 0; i < session->n_filters; i++) { session->filters[i].filter->obj->closeSession( session->filters[i].instance, session->filters[i].session); } for (i = 0; i < session->n_filters; i++) { session->filters[i].filter->obj->freeSession( session->filters[i].instance, session->filters[i].session); } free(session->filters); } free(session); succp = true; return_succp : return succp; }