static int routeQuery(ROUTER *instance, void *session, GWBUF *queue) { WEB_SESSION *wsession = (WEB_SESSION *)session; char *ptr; int i, found = 0; char *url; if ((url = gwbuf_get_property(queue, "URL")) == NULL) { respond_error(wsession, 404, "No URL available"); } ptr = strrchr(url, '/'); if (ptr) ptr++; else ptr = url; for (i = 0; pages[i].page; i++) { if (!strcmp(ptr, pages[i].page)) { (pages[i].fcn)(wsession); found = 1; } } if (!found) respond_error(wsession, 404, "Unrecognised URL received"); gwbuf_free(queue); return 0; }
int test2() { GWBUF *buffer; char len = 128; char query[129]; buffer = gwbuf_alloc(132); ss_info_dassert((buffer != NULL),"Buffer should not be null"); memset(query,';',128); memset(query+128,'\0',1); *((unsigned char*)buffer->start) = len; *((unsigned char*)buffer->start+1) = 0; *((unsigned char*)buffer->start+2) = 0; *((unsigned char*)buffer->start+3) = 1; *((unsigned char*)buffer->start+4) = 0x03; memcpy(buffer->start + 5,query,strlen(query)); char* result = modutil_get_SQL(buffer); ss_dassert(strcmp(result,query) == 0); gwbuf_free(buffer); free(result); ss_dfprintf(stderr, "\t..done\n"); return 0; }
static int test1() { GWBUF *buffer; char *(sql[100]); int result, length, residual; /* Poll tests */ ss_dfprintf(stderr, "testmodutil : Rudimentary tests."); buffer = gwbuf_alloc(100); ss_info_dassert(0 == modutil_is_SQL(buffer), "Default buffer should be diagnosed as not SQL"); /* There would ideally be some straightforward way to create a SQL buffer? */ ss_dfprintf(stderr, "\t..done\nExtract SQL from buffer"); ss_info_dassert(0 == modutil_extract_SQL(buffer, sql, &length), "Default buffer should fail"); ss_dfprintf(stderr, "\t..done\nExtract SQL from buffer different way?"); ss_info_dassert(0 == modutil_MySQL_Query(buffer, sql, &length, &residual), "Default buffer should fail"); ss_dfprintf(stderr, "\t..done\nReplace SQL in buffer"); ss_info_dassert(0 == modutil_replace_SQL(buffer, "select * from some_table;"), "Default buffer should fail"); ss_dfprintf(stderr, "\t..done\nTidy up."); gwbuf_free(buffer); ss_dfprintf(stderr, "\t..done\n"); return 0; }
/** * The routeQuery entry point. This is passed the query buffer * to which the filter should be applied. Once applied the * query should normally be passed to the downstream component * (filter or router) in the filter chain. * * If my_session->residual is set then duplicate that many bytes * and send them to the branch. * * If my_session->residual is zero then this must be a new request * Extract the SQL text if possible, match against that text and forward * the request. If the requets is not contained witin the packet we have * then set my_session->residual to the number of outstanding bytes * * @param instance The filter instance data * @param session The filter session * @param queue The query data */ static int routeQuery(FILTER *instance, void *session, GWBUF *queue) { TEE_INSTANCE *my_instance = (TEE_INSTANCE *)instance; TEE_SESSION *my_session = (TEE_SESSION *)session; char *ptr; int rval; GWBUF *buffer = NULL, *clone = NULL; unsigned char command = gwbuf_length(queue) >= 5 ? *((unsigned char*)queue->start + 4) : 1; #ifdef SS_DEBUG skygw_log_write(LOGFILE_TRACE,"Tee routeQuery: %d : %s", atomic_add(&debug_seq,1), ((char*)queue->start + 5)); #endif spinlock_acquire(&my_session->tee_lock); if(!my_session->active) { skygw_log_write(LOGFILE_TRACE, "Tee: Received a reply when the session was closed."); gwbuf_free(queue); spinlock_release(&my_session->tee_lock); return 0; } if(my_session->queue) { my_session->queue = gwbuf_append(my_session->queue,queue); buffer = modutil_get_next_MySQL_packet(&my_session->queue); } else { buffer = modutil_get_next_MySQL_packet(&queue); my_session->queue = queue; } if(buffer == NULL) { spinlock_release(&my_session->tee_lock); return 1; } clone = clone_query(my_instance, my_session,buffer); spinlock_release(&my_session->tee_lock); /* Reset session state */ if(!reset_session_state(my_session,buffer)) return 0; /** Route query downstream */ spinlock_acquire(&my_session->tee_lock); rval = route_single_query(my_instance,my_session,buffer,clone); spinlock_release(&my_session->tee_lock); return rval; }
/** * Free the memory associated with the session * * @param instance The filter instance * @param session The filter session */ static void freeSession(FILTER *instance, void *session) { TEE_SESSION *my_session = (TEE_SESSION *)session; SESSION* ses = my_session->branch_session; session_state_t state; #ifdef SS_DEBUG skygw_log_write(LOGFILE_TRACE,"Tee free: %d", atomic_add(&debug_seq,1)); #endif if (ses != NULL) { state = ses->state; if (state == SESSION_STATE_ROUTER_READY) { session_free(ses); } else if (state == SESSION_STATE_TO_BE_FREED) { /** Free branch router session */ ses->service->router->freeSession( ses->service->router_instance, ses->router_session); /** Free memory of branch client session */ ses->state = SESSION_STATE_FREE; free(ses); /** This indicates that branch session is not available anymore */ my_session->branch_session = NULL; } else if(state == SESSION_STATE_STOPPING) { orphan_session_t* orphan; if((orphan = malloc(sizeof(orphan_session_t))) == NULL) { skygw_log_write(LOGFILE_ERROR,"Error : Failed to " "allocate memory for orphan session struct, " "child session might leak memory."); }else{ orphan->session = ses; spinlock_acquire(&orphanLock); orphan->next = allOrphans; allOrphans = orphan; spinlock_release(&orphanLock); } } } if (my_session->dummy_filterdef) { filter_free(my_session->dummy_filterdef); } if(my_session->tee_replybuf) gwbuf_free(my_session->tee_replybuf); free(session); orphan_free(NULL); return; }
/** * Trim bytes from the end of a GWBUF structure that may be the first * in a list. If the buffer has n_bytes or less then it will be freed and * the next buffer in the list will be returned, or if none, NULL. * * @param head The buffer to trim * @param n_bytes The number of bytes to trim off * @return The buffer chain or NULL if buffer chain now empty */ GWBUF * gwbuf_rtrim(GWBUF *head, unsigned int n_bytes) { GWBUF *rval = head; CHK_GWBUF(head); GWBUF_RTRIM(head, n_bytes); CHK_GWBUF(head); if (GWBUF_EMPTY(head)) { rval = head->next; gwbuf_free(head); } return rval; }
/** * Consume data from a buffer in the linked list. The assumption is to consume * n bytes from the buffer chain. * * If after consuming the bytes from the first buffer that buffer becomes * empty it will be freed and the linked list updated. * * The return value is the new head of the linked list. * * This call should be made with the caller holding the lock for the linked * list. * * @param head The head of the linked list * @param length The amount of data to consume * @return The head of the linked list */ GWBUF * gwbuf_consume(GWBUF *head, unsigned int length) { GWBUF *rval = head; CHK_GWBUF(head); GWBUF_CONSUME(head, length); CHK_GWBUF(head); if (GWBUF_EMPTY(head)) { rval = head->next; gwbuf_free(head); } return rval; }
/** * Free the memory associated with the session * * @param instance The filter instance * @param session The filter session */ static void freeSession(FILTER *instance, void *session) { TEE_SESSION *my_session = (TEE_SESSION *) session; SESSION* ses = my_session->branch_session; session_state_t state; #ifdef SS_DEBUG MXS_INFO("Tee free: %d", atomic_add(&debug_seq, 1)); #endif if (ses != NULL) { state = ses->state; if (state == SESSION_STATE_ROUTER_READY) { session_free(ses); } else if (state == SESSION_STATE_TO_BE_FREED) { /** Free branch router session */ ses->service->router->freeSession( ses->service->router_instance, ses->router_session); /** Free memory of branch client session */ ses->state = SESSION_STATE_FREE; free(ses); /** This indicates that branch session is not available anymore */ my_session->branch_session = NULL; } else if (state == SESSION_STATE_STOPPING) { create_orphan(ses); } } if (my_session->dummy_filterdef) { filter_free(my_session->dummy_filterdef); } if (my_session->tee_replybuf) { gwbuf_free(my_session->tee_replybuf); } free(session); orphan_free(NULL); return; }
/** * Route the main query downstream along the main filter chain and possibly route * a clone of the buffer to the branch session. If the clone buffer is NULL, nothing * is routed to the branch session. * @param my_instance Tee instance * @param my_session Tee session * @param buffer Main buffer * @param clone Cloned buffer * @return 1 on success, 0 on failure. */ int route_single_query(TEE_INSTANCE* my_instance, TEE_SESSION* my_session, GWBUF* buffer, GWBUF* clone) { int rval = 0; if(!my_session->active || my_session->branch_session == NULL || my_session->branch_session->state != SESSION_STATE_ROUTER_READY) { rval = 0; my_session->active = 0; return rval; } rval = my_session->down.routeQuery(my_session->down.instance, my_session->down.session, buffer); if (clone) { my_session->n_duped++; if (my_session->branch_session->state == SESSION_STATE_ROUTER_READY) { SESSION_ROUTE_QUERY(my_session->branch_session, clone); } else { /** Close tee session */ my_session->active = 0; rval = 0; LOGIF(LT, (skygw_log_write( LOGFILE_TRACE, "Closed tee filter session: Child session in invalid state."))); gwbuf_free(clone); } } else { if (my_session->active) { LOGIF(LT, (skygw_log_write( LOGFILE_TRACE, "Closed tee filter session: Child session is NULL."))); my_session->active = 0; rval = 0; } my_session->n_rejected++; } return rval; }
int main(int argc, char** argv) { unsigned int psize; GWBUF* qbuff; char *tok; char readbuff[4092]; FILE* infile; FILE* outfile; if(argc != 3){ printf("Usage: canonizer <input file> <output file>\n"); return 1; } if(mysql_library_init(num_elements, server_options, server_groups)){ printf("Embedded server init failed.\n"); return 1; } infile = fopen(argv[1],"rb"); outfile = fopen(argv[2],"wb"); if(infile == NULL || outfile == NULL){ printf("Opening files failed.\n"); return 1; } while(!feof(infile)) { fgets(readbuff,4092,infile); psize = strlen(readbuff); if(psize < 4092){ qbuff = gwbuf_alloc(psize + 7); *(qbuff->sbuf->data + 0) = (unsigned char)psize; *(qbuff->sbuf->data + 1) = (unsigned char)(psize>>8); *(qbuff->sbuf->data + 2) = (unsigned char)(psize>>16); *(qbuff->sbuf->data + 4) = 0x03; memcpy(qbuff->start + 5,readbuff,psize + 1); parse_query(qbuff); tok = skygw_get_canonical(qbuff); fprintf(outfile,"%s\n",tok); free(tok); gwbuf_free(qbuff); } }
/** * Route the main query downstream along the main filter chain and possibly route * a clone of the buffer to the branch session. If the clone buffer is NULL, nothing * is routed to the branch session. * @param my_instance Tee instance * @param my_session Tee session * @param buffer Main buffer * @param clone Cloned buffer * @return 1 on success, 0 on failure. */ int route_single_query(TEE_INSTANCE* my_instance, TEE_SESSION* my_session, GWBUF* buffer, GWBUF* clone) { int rval = 0; if (!my_session->active || my_session->branch_session == NULL || my_session->branch_session->state != SESSION_STATE_ROUTER_READY) { rval = 0; my_session->active = 0; return rval; } if (clone == NULL) { /** We won't be expecting any response from the child branch */ my_session->waiting[CHILD] = false; my_session->multipacket[CHILD] = false; my_session->eof[CHILD] = 2; my_session->n_rejected++; } rval = my_session->down.routeQuery(my_session->down.instance, my_session->down.session, buffer); if (clone) { my_session->n_duped++; if (my_session->branch_session->state == SESSION_STATE_ROUTER_READY) { SESSION_ROUTE_QUERY(my_session->branch_session, clone); } else { /** Close tee session */ my_session->active = 0; rval = 0; MXS_INFO("Closed tee filter session: Child session in invalid state."); gwbuf_free(clone); } } return rval; }
void free_buffers() { if(instance.buffer){ int i; for(i = 0;i<instance.buffer_count;i++){ gwbuf_free(instance.buffer[i]); } free(instance.buffer); instance.buffer = NULL; instance.buffer_count = 0; } if(instance.infile >= 0){ close(instance.infile); free(instance.infile_name); instance.infile = -1; } }
/** * We have data from the client, this is a HTTP URL * * @param instance The router instance * @param session The router session returned from the newSession call * @param queue The queue of data buffers to route * @return The number of bytes sent */ static int handle_url(INFO_INSTANCE *instance, INFO_SESSION *session, GWBUF *queue) { char *uri; int i; RESULTSET *set; uri = (char *)GWBUF_DATA(queue); for (i = 0; supported_uri[i].uri; i++) { if (strcmp(uri, supported_uri[i].uri) == 0) { set = (*supported_uri[i].func)(); resultset_stream_json(set, session->dcb); resultset_free(set); } } gwbuf_free(queue); return 1; }
/** * Consume data from a buffer in the linked list. The assumption is to consume * n bytes from the buffer chain. * * If after consuming the bytes from the first buffer that buffer becomes * empty it will be freed and the linked list updated. * * The return value is the new head of the linked list. * * This call should be made with the caller holding the lock for the linked * list. * * @param head The head of the linked list * @param length The amount of data to consume * @return The head of the linked list */ GWBUF * gwbuf_consume(GWBUF *head, unsigned int length) { GWBUF *rval = head; CHK_GWBUF(head); GWBUF_CONSUME(head, length); CHK_GWBUF(head); if (GWBUF_EMPTY(head)) { rval = head->next; if (head->next) head->next->tail = head->tail; gwbuf_free(head); } ss_dassert(rval == NULL || (rval->end > rval->start)); return rval; }
void work_buffer(void* thr_num) { unsigned int index = instance.session_count; GWBUF* fake_ok = gen_packet(PACKET_OK); while(instance.running){ pthread_mutex_lock(&instance.work_mtx); pthread_mutex_unlock(&instance.work_mtx); index = atomic_add(&instance.sess_ind,1); if(instance.running && index < instance.session_count && instance.buff_ind < instance.buffer_count) { struct timespec ts1; ts1.tv_sec = 0; if(instance.head->instance->routeQuery(instance.head->filter, instance.head->session[index], instance.buffer[instance.buff_ind]) == 0){ if(instance.outfile > 0){ const char* msg = "Query returned 0.\n"; write(instance.outfile,msg,strlen(msg)); } } if(instance.tail->instance->clientReply){ instance.tail->instance->clientReply(instance.tail->filter, instance.tail->session[index], fake_ok); } atomic_add(&instance.last_ind,1); ts1.tv_nsec = 1000*instance.rt_delay*1000000; nanosleep(&ts1, NULL); } } gwbuf_free(fake_ok); }
int main(int argc, char** argv) { if(argc < 3){ fprintf(stderr,"Usage: classify <input> <expected output>"); return 1; } int rd = 0,buffsz = getpagesize(),strsz = 0,ex_val = 0; char buffer[1024], *strbuff = (char*)calloc(buffsz,sizeof(char)); FILE *input,*expected; if(mysql_library_init(num_elements, server_options, server_groups)) { printf("Error: Cannot initialize Embedded Library."); return 1; } input = fopen(argv[1],"rb"); if(input == NULL) { printf("Error: Failed to open input file %s", argv[1]); return 1; } expected = fopen(argv[2],"rb"); if(expected == NULL) { fclose(input); printf("Error: Failed to open expected output file %s", argv[2]); return 1; } while((rd = fread(buffer,sizeof(char),1023,input))){ /**Fill the read buffer*/ if(strsz + rd >= buffsz){ char* tmp = realloc(strbuff,(buffsz*2)*sizeof(char)); if(tmp == NULL){ free(strbuff); fclose(input); fclose(expected); mysql_library_end(); fprintf(stderr,"Error: Memory allocation failed."); return 1; } strbuff = tmp; buffsz *= 2; } memcpy(strbuff+strsz,buffer,rd); strsz += rd; *(strbuff+strsz) = '\0'; char *tok,*nlptr; /**Remove newlines*/ while((nlptr = strpbrk(strbuff,"\n")) != NULL && (nlptr - strbuff) < strsz){ memmove(nlptr,nlptr+1,strsz - (nlptr + 1 - strbuff)); strsz -= 1; } /**Parse read buffer for full queries*/ while(strpbrk(strbuff,";") != NULL){ tok = strpbrk(strbuff,";"); unsigned int qlen = tok - strbuff + 1; GWBUF* buff = gwbuf_alloc(qlen+6); *((unsigned char*)(buff->start)) = qlen; *((unsigned char*)(buff->start + 1)) = (qlen >> 8); *((unsigned char*)(buff->start + 2)) = (qlen >> 16); *((unsigned char*)(buff->start + 3)) = 0x00; *((unsigned char*)(buff->start + 4)) = 0x03; memcpy(buff->start+5, strbuff, qlen); memmove(strbuff,tok + 1, strsz - qlen); strsz -= qlen; memset(strbuff + strsz,0,buffsz - strsz); skygw_query_type_t type = query_classifier_get_type(buff); char qtypestr[64]; char expbuff[256]; int expos = 0; while((rd = fgetc(expected)) != '\n' && !feof(expected)){ expbuff[expos++] = rd; } expbuff[expos] = '\0'; if(type == QUERY_TYPE_UNKNOWN){ sprintf(qtypestr,"QUERY_TYPE_UNKNOWN"); } if(type & QUERY_TYPE_LOCAL_READ){ sprintf(qtypestr,"QUERY_TYPE_LOCAL_READ"); } if(type & QUERY_TYPE_READ){ sprintf(qtypestr,"QUERY_TYPE_READ"); } if(type & QUERY_TYPE_WRITE){ sprintf(qtypestr,"QUERY_TYPE_WRITE"); } if(type & QUERY_TYPE_MASTER_READ){ sprintf(qtypestr,"QUERY_TYPE_MASTER_READ"); } if(type & QUERY_TYPE_SESSION_WRITE){ sprintf(qtypestr,"QUERY_TYPE_SESSION_WRITE"); } if(type & QUERY_TYPE_USERVAR_READ){ sprintf(qtypestr,"QUERY_TYPE_USERVAR_READ"); } if(type & QUERY_TYPE_SYSVAR_READ){ sprintf(qtypestr,"QUERY_TYPE_SYSVAR_READ"); } if(type & QUERY_TYPE_GSYSVAR_READ){ sprintf(qtypestr,"QUERY_TYPE_GSYSVAR_READ"); } if(type & QUERY_TYPE_GSYSVAR_WRITE){ sprintf(qtypestr,"QUERY_TYPE_GSYSVAR_WRITE"); } if(type & QUERY_TYPE_BEGIN_TRX){ sprintf(qtypestr,"QUERY_TYPE_BEGIN_TRX"); } if(type & QUERY_TYPE_ENABLE_AUTOCOMMIT){ sprintf(qtypestr,"QUERY_TYPE_ENABLE_AUTOCOMMIT"); } if(type & QUERY_TYPE_DISABLE_AUTOCOMMIT){ sprintf(qtypestr,"QUERY_TYPE_DISABLE_AUTOCOMMIT"); } if(type & QUERY_TYPE_ROLLBACK){ sprintf(qtypestr,"QUERY_TYPE_ROLLBACK"); } if(type & QUERY_TYPE_COMMIT){ sprintf(qtypestr,"QUERY_TYPE_COMMIT"); } if(type & QUERY_TYPE_PREPARE_NAMED_STMT){ sprintf(qtypestr,"QUERY_TYPE_PREPARE_NAMED_STMT"); } if(type & QUERY_TYPE_PREPARE_STMT){ sprintf(qtypestr,"QUERY_TYPE_PREPARE_STMT"); } if(type & QUERY_TYPE_EXEC_STMT){ sprintf(qtypestr,"QUERY_TYPE_EXEC_STMT"); } if(type & QUERY_TYPE_CREATE_TMP_TABLE){ sprintf(qtypestr,"QUERY_TYPE_CREATE_TMP_TABLE"); } if(type & QUERY_TYPE_READ_TMP_TABLE){ sprintf(qtypestr,"QUERY_TYPE_READ_TMP_TABLE"); } if(strcmp(qtypestr,expbuff) != 0){ printf("Error in output: '%s' was expected but got '%s'",expbuff,qtypestr); ex_val = 1; } gwbuf_free(buff); } } fclose(input); fclose(expected); mysql_library_end(); free(strbuff); return ex_val; }
/** * The routeQuery entry point. This is passed the query buffer * to which the filter should be applied. Once processed the * query is passed to the downstream component * (filter or router) in the filter chain. * * The Luafilter calls the routeQuery functions of both the session and the global script. * The query is passed as a string parameter to the routeQuery Lua function and * the return values of the session specific function, if any were returned, * are interpreted. If the first value is bool, it is interpreted as a decision * whether to route the query or to send an error packet to the client. * If it is a string, the current query is replaced with the return value and * the query will be routed. If nil is returned, the query is routed normally. * * @param instance The filter instance data * @param session The filter session * @param queue The query data */ static int routeQuery(FILTER *instance, void *session, GWBUF *queue) { LUA_SESSION *my_session = (LUA_SESSION *) session; LUA_INSTANCE *my_instance = (LUA_INSTANCE *) instance; DCB* dcb = my_session->session->client_dcb; char *fullquery = NULL, *ptr; bool route = true; GWBUF* forward = queue; int rc = 0; if (modutil_is_SQL(queue) || modutil_is_SQL_prepare(queue)) { fullquery = modutil_get_SQL(queue); if (fullquery && my_session->lua_state) { spinlock_acquire(&my_session->lock); lua_getglobal(my_session->lua_state, "routeQuery"); lua_pushlstring(my_session->lua_state, fullquery, strlen(fullquery)); if (lua_pcall(my_session->lua_state, 1, 1, 0)) { MXS_ERROR("luafilter: Session scope call to 'routeQuery' failed: '%s'.", lua_tostring(my_session->lua_state, -1)); } else if (lua_gettop(my_session->lua_state)) { if (lua_isstring(my_session->lua_state, -1)) { if (forward) { gwbuf_free(forward); } forward = modutil_create_query((char*) lua_tostring(my_session->lua_state, -1)); } else if (lua_isboolean(my_session->lua_state, -1)) { route = lua_toboolean(my_session->lua_state, -1); } } spinlock_release(&my_session->lock); } if (my_instance->global_lua_state) { spinlock_acquire(&my_instance->lock); lua_getglobal(my_instance->global_lua_state, "routeQuery"); lua_pushlstring(my_instance->global_lua_state, fullquery, strlen(fullquery)); if (lua_pcall(my_instance->global_lua_state, 1, 0, 0)) { MXS_ERROR("luafilter: Global scope call to 'routeQuery' failed: '%s'.", lua_tostring(my_session->lua_state, -1)); } else if (lua_gettop(my_instance->global_lua_state)) { if (lua_isstring(my_instance->global_lua_state, -1)) { if (forward) { gwbuf_free(forward); } forward = modutil_create_query((char*) lua_tostring(my_instance->global_lua_state, -1)); } else if (lua_isboolean(my_instance->global_lua_state, -1)) { route = lua_toboolean(my_instance->global_lua_state, -1); } } spinlock_release(&my_instance->lock); } free(fullquery); } if (!route) { gwbuf_free(queue); GWBUF* err = modutil_create_mysql_err_msg(1, 0, 1045, "28000", "Access denied."); rc = dcb->func.write(dcb, err); } else { rc = my_session->down.routeQuery(my_session->down.instance, my_session->down.session, forward); } return rc; }
/** * The clientReply entry point. This is passed the response buffer * to which the filter should be applied. Once processed the * query is passed to the upstream component * (filter or router) in the filter chain. * * @param instance The filter instance data * @param session The filter session * @param reply The response data */ static int clientReply(FILTER* instance, void *session, GWBUF *reply) { int rc, branch, eof; TEE_SESSION *my_session = (TEE_SESSION *) session; bool route = false, mpkt; GWBUF *complete = NULL; unsigned char *ptr; uint16_t flags = 0; int min_eof = my_session->command != 0x04 ? 2 : 1; int more_results = 0; #ifdef SS_DEBUG int prev_debug_seq = atomic_add(&debug_seq, 1); ptr = (unsigned char*) reply->start; MXS_INFO("Tee clientReply [%s] [%s] [%s]: %d", instance ? "parent" : "child", my_session->active ? "open" : "closed", PTR_IS_ERR(ptr) ? "ERR" : PTR_IS_OK(ptr) ? "OK" : "RSET", prev_debug_seq); #endif spinlock_acquire(&my_session->tee_lock); if (!my_session->active) { MXS_INFO("Tee: Failed to return reply, session is closed"); gwbuf_free(reply); rc = 0; if (my_session->waiting[PARENT]) { GWBUF* errbuf = modutil_create_mysql_err_msg(1, 0, 1, "0000", "Session closed."); my_session->waiting[PARENT] = false; my_session->up.clientReply(my_session->up.instance, my_session->up.session, errbuf); } goto retblock; } branch = instance == NULL ? CHILD : PARENT; my_session->tee_partials[branch] = gwbuf_append(my_session->tee_partials[branch], reply); my_session->tee_partials[branch] = gwbuf_make_contiguous(my_session->tee_partials[branch]); complete = modutil_get_complete_packets(&my_session->tee_partials[branch]); if (complete == NULL) { /** Incomplete packet */ MXS_DEBUG("tee.c: Incomplete packet, " "waiting for a complete packet before forwarding."); rc = 1; goto retblock; } complete = gwbuf_make_contiguous(complete); if (my_session->tee_partials[branch] && GWBUF_EMPTY(my_session->tee_partials[branch])) { gwbuf_free(my_session->tee_partials[branch]); my_session->tee_partials[branch] = NULL; } ptr = (unsigned char*) complete->start; if (my_session->replies[branch] == 0) { MXS_INFO("Tee: First reply to a query for [%s].", branch == PARENT ? "PARENT" : "CHILD"); /* Reply is in a single packet if it is an OK, ERR or LOCAL_INFILE packet. * Otherwise the reply is a result set and the amount of packets is unknown. */ if (PTR_IS_ERR(ptr) || PTR_IS_LOCAL_INFILE(ptr) || PTR_IS_OK(ptr) || !my_session->multipacket[branch]) { my_session->waiting[branch] = false; my_session->multipacket[branch] = false; if (PTR_IS_OK(ptr)) { flags = get_response_flags(ptr, true); more_results = (flags & 0x08) && my_session->client_multistatement; if (more_results) { MXS_INFO("Tee: [%s] waiting for more results.", branch == PARENT ? "PARENT" : "CHILD"); } } } #ifdef SS_DEBUG else { MXS_DEBUG("tee.c: [%ld] Waiting for a result set from %s session.", my_session->d_id, branch == PARENT ? "parent" : "child"); } #endif } if (my_session->waiting[branch]) { eof = modutil_count_signal_packets(complete, my_session->use_ok, my_session->eof[branch] > 0, &more_results); more_results &= my_session->client_multistatement; my_session->eof[branch] += eof; if (my_session->eof[branch] >= min_eof) { #ifdef SS_DEBUG MXS_DEBUG("tee.c [%ld] %s received last EOF packet", my_session->d_id, branch == PARENT ? "parent" : "child"); #endif my_session->waiting[branch] = more_results; if (more_results) { my_session->eof[branch] = 0; } } } if (branch == PARENT) { my_session->tee_replybuf = gwbuf_append(my_session->tee_replybuf, complete); } else { gwbuf_free(complete); } my_session->replies[branch]++; rc = 1; mpkt = my_session->multipacket[PARENT] || my_session->multipacket[CHILD]; if (my_session->tee_replybuf != NULL) { if (my_session->branch_session == NULL) { rc = 0; gwbuf_free(my_session->tee_replybuf); my_session->tee_replybuf = NULL; MXS_ERROR("Tee child session was closed."); } if (mpkt) { if (my_session->waiting[PARENT]) { route = true; } else if (my_session->eof[PARENT] >= min_eof && my_session->eof[CHILD] >= min_eof) { route = true; #ifdef SS_DEBUG MXS_DEBUG("tee.c:[%ld] Routing final packet of response set.", my_session->d_id); #endif } } else if (!my_session->waiting[PARENT] && !my_session->waiting[CHILD]) { #ifdef SS_DEBUG MXS_DEBUG("tee.c:[%ld] Routing single packet response.", my_session->d_id); #endif route = true; } } if (route) { #ifdef SS_DEBUG MXS_DEBUG("tee.c:[%ld] Routing buffer '%p' parent(waiting [%s] replies [%d] eof[%d])" " child(waiting [%s] replies[%d] eof [%d])", my_session->d_id, my_session->tee_replybuf, my_session->waiting[PARENT] ? "true" : "false", my_session->replies[PARENT], my_session->eof[PARENT], my_session->waiting[CHILD] ? "true" : "false", my_session->replies[CHILD], my_session->eof[CHILD]); #endif rc = my_session->up.clientReply(my_session->up.instance, my_session->up.session, my_session->tee_replybuf); my_session->tee_replybuf = NULL; } if (my_session->queue && !my_session->waiting[PARENT] && !my_session->waiting[CHILD]) { GWBUF* buffer = modutil_get_next_MySQL_packet(&my_session->queue); GWBUF* clone = clone_query(my_session->instance, my_session, buffer); reset_session_state(my_session, buffer); route_single_query(my_session->instance, my_session, buffer, clone); MXS_INFO("tee: routing queued query"); } retblock: spinlock_release(&my_session->tee_lock); return rc; }
/** * Loads a query from a file *@return 0 if successful, 1 if an error occurred */ int load_query() { char** query_list = NULL; char* buffer = NULL; int i, qcount = 0, qbuff_sz = 10, rval = 0; int offset = 0; unsigned int qlen = 0; buffer = (char*)calloc(4092,sizeof(char)); if(buffer == NULL){ printf("Error: cannot allocate enough memory.\n"); MXS_ERROR("cannot allocate enough memory.\n"); return 1; } query_list = calloc(qbuff_sz,sizeof(char*)); if(query_list == NULL){ printf("Error: cannot allocate enough memory.\n"); MXS_ERROR("cannot allocate enough memory.\n"); free(buffer); return 1; } while((offset = fdgets(instance.infile,buffer,4092))){ if(qbuff_sz <= qcount){ char** tmpbuff = realloc(query_list,sizeof(char*)*qbuff_sz*2); if(tmpbuff == NULL){ printf("Error: cannot allocate enough memory.\n"); MXS_ERROR("cannot allocate enough memory.\n"); rval = 1; goto retblock; } query_list = tmpbuff; qbuff_sz *= 2; } query_list[qcount] = calloc((offset + 1),sizeof(char)); strcpy(query_list[qcount],buffer); offset = 0; qcount++; } /**TODO check what messes up the first querystring*/ GWBUF** tmpbff = malloc(sizeof(GWBUF*)*(qcount + 1)); if(tmpbff){ for(i = 0;i<qcount;i++){ tmpbff[i] = gwbuf_alloc(strlen(query_list[i]) + 6); if(tmpbff[i] == NULL) { printf("Error: cannot allocate a new buffer.\n"); MXS_ERROR("cannot allocate a new buffer.\n"); int x; for(x = 0;x<i;x++) { gwbuf_free(tmpbff[x]); } free(tmpbff); rval = 1; goto retblock; } gwbuf_set_type(tmpbff[i],GWBUF_TYPE_MYSQL); strcpy((char*)(tmpbff[i]->start + 5),query_list[i]); qlen = strlen(query_list[i]) + 1; tmpbff[i]->sbuf->data[0] = qlen; tmpbff[i]->sbuf->data[1] = (qlen << 8); tmpbff[i]->sbuf->data[2] = (qlen << 16); tmpbff[i]->sbuf->data[3] = 0x00; tmpbff[i]->sbuf->data[4] = 0x03; } tmpbff[qcount] = NULL; instance.buffer = tmpbff; }else{ printf("Error: cannot allocate enough memory for buffers.\n"); MXS_ERROR("cannot allocate enough memory for buffers.\n"); free_buffers(); rval = 1; goto retblock; } if(qcount < 1){ rval = 1; goto retblock; } instance.buffer_count = qcount; retblock: for(i = 0;i<qcount;i++) { free(query_list[i]); } free(query_list); free(buffer); return rval; }