GWBUF *gwbuf_clone_portion( GWBUF *buf, size_t start_offset, size_t length) { GWBUF* clonebuf; CHK_GWBUF(buf); ss_dassert(start_offset+length <= GWBUF_LENGTH(buf)); if ((clonebuf = (GWBUF *)malloc(sizeof(GWBUF))) == NULL) { ss_dassert(clonebuf != NULL); LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Memory allocation failed due to %s.", strerror(errno)))); return NULL; } atomic_add(&buf->sbuf->refcount, 1); clonebuf->sbuf = buf->sbuf; clonebuf->gwbuf_type = buf->gwbuf_type; /*< clone info bits too */ clonebuf->start = (void *)((char*)buf->start+start_offset); clonebuf->end = (void *)((char *)clonebuf->start+length); clonebuf->gwbuf_type = buf->gwbuf_type; /*< clone the type for now */ clonebuf->properties = NULL; clonebuf->hint = NULL; clonebuf->gwbuf_info = buf->gwbuf_info; clonebuf->gwbuf_bufobj = buf->gwbuf_bufobj; clonebuf->next = NULL; clonebuf->tail = clonebuf; CHK_GWBUF(clonebuf); return clonebuf; }
/** * Creates MySQL protocol structure * * @param dcb * Must be non-NULL. * @param fd * * @return * * * @details Protocol structure does not have fd because dcb is not * connected yet. * */ MySQLProtocol* mysql_protocol_init( DCB* dcb, int fd) { MySQLProtocol* p; p = (MySQLProtocol *) calloc(1, sizeof(MySQLProtocol)); ss_dassert(p != NULL); if (p == NULL) { int eno = errno; errno = 0; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "%lu [mysql_init_protocol] MySQL protocol init failed : " "memory allocation due error %d, %s.", pthread_self(), eno, strerror(eno)))); goto return_p; } p->state = MYSQL_ALLOC; #if defined(SS_DEBUG) p->protocol_chk_top = CHK_NUM_PROTOCOL; p->protocol_chk_tail = CHK_NUM_PROTOCOL; #endif /*< Assign fd with protocol */ p->fd = fd; p->owner_dcb = dcb; CHK_PROTOCOL(p); return_p: return p; }
/** * Increment the usage count of a gateway buffer. This gets a new * GWBUF structure that shares the actual data with the existing * GWBUF structure but allows for the data copy to be avoided and * also for each GWBUF to point to different portions of the same * SHARED_BUF. * * @param buf The buffer to use * @return A new GWBUF structure */ GWBUF * gwbuf_clone(GWBUF *buf) { GWBUF *rval; if ((rval = (GWBUF *)calloc(1,sizeof(GWBUF))) == NULL) { ss_dassert(rval != NULL); LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Memory allocation failed due to %s.", strerror(errno)))); return NULL; } atomic_add(&buf->sbuf->refcount, 1); rval->sbuf = buf->sbuf; rval->start = buf->start; rval->end = buf->end; rval->gwbuf_type = buf->gwbuf_type; rval->gwbuf_info = buf->gwbuf_info; rval->gwbuf_bufobj = buf->gwbuf_bufobj; rval->tail = rval; CHK_GWBUF(rval); return rval; }
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; }
/** * List the defined services in a tabular format. * * @param dcb DCB to print the service list to. */ void dListServices(DCB *dcb) { SERVICE *ptr; spinlock_acquire(&service_spin); ptr = allServices; if (ptr) { dcb_printf(dcb, "Services.\n"); dcb_printf(dcb, "--------------------------+----------------------+--------+---------------\n"); dcb_printf(dcb, "%-25s | %-20s | #Users | Total Sessions\n", "Service Name", "Router Module"); dcb_printf(dcb, "--------------------------+----------------------+--------+---------------\n"); } while (ptr) { ss_dassert(ptr->stats.n_current >= 0); dcb_printf(dcb, "%-25s | %-20s | %6d | %5d\n", ptr->name, ptr->routerModule, ptr->stats.n_current, ptr->stats.n_sessions); ptr = ptr->next; } if (allServices) dcb_printf(dcb, "--------------------------+----------------------+--------+---------------\n\n"); spinlock_release(&service_spin); }
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; }
/** * Remove a descriptor from the set of descriptors within the * polling environment. * The state change command may fail because concurrent threads may call * dcb_set_state simultaneously and the conflict is prevented in dcb_set_state. * * @param dcb The descriptor to remove * @return -1 on error or 0 on success */ int poll_remove_dcb(DCB *dcb) { struct epoll_event ev; int rc = -1; dcb_state_t old_state = DCB_STATE_UNDEFINED; dcb_state_t new_state = DCB_STATE_NOPOLLING; CHK_DCB(dcb); /*< It is possible that dcb has already been removed from the set */ if (dcb->state != DCB_STATE_POLLING) { if (dcb->state == DCB_STATE_NOPOLLING || dcb->state == DCB_STATE_ZOMBIE) { rc = 0; } goto return_rc; } /*< * Set state to NOPOLLING and remove dcb from poll set. */ if (dcb_set_state(dcb, new_state, &old_state)) { /** * Only positive fds can be removed from epoll set. */ if (dcb->fd > 0) { rc = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, dcb->fd, &ev); if (rc != 0) { int eno = errno; errno = 0; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : epoll_ctl failed due %d, %s.", eno, strerror(eno)))); } ss_dassert(rc == 0); /*< trap in debug */ } } /*< * This call was redundant, but the end result is correct. */ else if (old_state == new_state) { rc = 0; goto return_rc; } /*< Set bit for each maxscale thread */ bitmask_copy(&dcb->memdata.bitmask, poll_bitmask()); rc = 0; return_rc: return rc; }
/** * Allocate a new gateway buffer structure of size bytes. * * For now we allocate memory directly from malloc for buffer the management * structure and the actual data buffer itself. We may swap at a future date * to a more effecient mechanism. * * @param size The size in bytes of the data area required * @return Pointer to the buffer structure or NULL if memory could not * be allocated. */ GWBUF * gwbuf_alloc(unsigned int size) { GWBUF *rval; SHARED_BUF *sbuf; /* Allocate the buffer header */ if ((rval = (GWBUF *)malloc(sizeof(GWBUF))) == NULL) { goto retblock;; } /* Allocate the shared data buffer */ if ((sbuf = (SHARED_BUF *)malloc(sizeof(SHARED_BUF))) == NULL) { free(rval); rval = NULL; goto retblock; } /* Allocate the space for the actual data */ if ((sbuf->data = (unsigned char *)malloc(size)) == NULL) { ss_dassert(sbuf->data != NULL); free(rval); free(sbuf); rval = NULL; goto retblock; } spinlock_init(&rval->gwbuf_lock); rval->start = sbuf->data; rval->end = (void *)((char *)rval->start+size); sbuf->refcount = 1; rval->sbuf = sbuf; rval->next = NULL; rval->tail = rval; rval->hint = NULL; rval->properties = NULL; rval->gwbuf_type = GWBUF_TYPE_UNDEFINED; rval->gwbuf_info = GWBUF_INFO_NONE; rval->gwbuf_bufobj = NULL; CHK_GWBUF(rval); retblock: if (rval == NULL) { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Memory allocation failed due to %s.", strerror(errno)))); } return rval; }
/** * Error Reply routine * * The routine will reply to client errors and/or closing the session * or try to open a new backend connection. * * @param instance The router instance * @param router_session The router session * @param message The error message to reply * @param backend_dcb The backend DCB * @param action The action: REPLY, REPLY_AND_CLOSE, NEW_CONNECTION * */ static void errorReply( ROUTER *instance, void *router_session, char *message, DCB *backend_dcb, int action) { DCB *client = NULL; SESSION *session = backend_dcb->session; client = session->client; ss_dassert(client != NULL); }
/** * Trim bytes form the end of a GWBUF structure. If the * buffer has n_bytes or less then it will be freed and * NULL will be returned. * * This routine assumes the buffer is not part of a chain * * @param buf The buffer to trim * @param n_bytes The number of bytes to trim off * @return The buffer chain or NULL if buffer has <= n_bytes */ GWBUF * gwbuf_trim(GWBUF *buf, unsigned int n_bytes) { ss_dassert(buf->next == NULL); if (GWBUF_LENGTH(buf) <= n_bytes) { gwbuf_consume(buf, GWBUF_LENGTH(buf)); return NULL; } buf->end = (void *)((char *)buf->end - n_bytes); return buf; }
/** * Error Handler routine * * The routine will handle errors that occurred in backend writes. * * @param instance The router instance * @param router_session The router session * @param message The error message to reply * @param backend_dcb The backend DCB * @param action The action: REPLY, REPLY_AND_CLOSE, NEW_CONNECTION * */ static void handleError( ROUTER *instance, void *router_session, GWBUF *errbuf, DCB *backend_dcb, error_action_t action, bool *succp) { DCB *client = NULL; SESSION *session = backend_dcb->session; client = session->client; ss_dassert(client != NULL); }
/** * Client Reply routine * * The routine will reply to client data from backend server * * @param instance The router instance * @param router_session The router session * @param backend_dcb The backend DCB * @param queue The GWBUF with reply data */ static void clientReply( ROUTER *instance, void *router_session, GWBUF *queue, DCB *backend_dcb) { DCB *client ; client = backend_dcb->session->client; ss_dassert(client != NULL); SESSION_ROUTE_REPLY(backend_dcb->session, queue); }
/** * Client Reply routine * * The routine will reply to client data from backend server * * @param instance The router instance * @param router_session The router session * @param backend_dcb The backend DCB * @param queue The GWBUF with reply data */ static void clientReply( ROUTER *instance, void *router_session, GWBUF *queue, DCB *backend_dcb) { DCB *client = NULL; client = backend_dcb->session->client; ss_dassert(client != NULL); client->func.write(client, queue); }
/** * Returns pointer to GWBUF of a requested type. * As of 10.3.14 only MySQL to plain text conversion is supported. * Return NULL if conversion between types is not supported or due lacking * type information. */ GWBUF *gwbuf_clone_transform( GWBUF * head, gwbuf_type_t targettype) { gwbuf_type_t src_type; GWBUF* clonebuf; CHK_GWBUF(head); src_type = head->gwbuf_type; if (targettype == GWBUF_TYPE_UNDEFINED || src_type == GWBUF_TYPE_UNDEFINED || src_type == GWBUF_TYPE_PLAINSQL || targettype == src_type) { clonebuf = NULL; goto return_clonebuf; } switch (src_type) { case GWBUF_TYPE_MYSQL: if (targettype == GWBUF_TYPE_PLAINSQL) { /** Crete reference to string part of buffer */ clonebuf = gwbuf_clone_portion( head, 5, GWBUF_LENGTH(head)-5); ss_dassert(clonebuf != NULL); /** Overwrite the type with new format */ clonebuf->gwbuf_type = targettype; } else { clonebuf = NULL; } break; default: clonebuf = NULL; break; } /*< switch (src_type) */ return_clonebuf: return clonebuf; }
/** * Returns pointer to GWBUF of a requested type. * As of 10.3.14 only MySQL to plain text conversion is supported. * Return NULL if conversion between types is not supported or due lacking * type information. */ GWBUF *gwbuf_clone_transform( GWBUF * head, gwbuf_type_t targettype) { gwbuf_type_t src_type; GWBUF* clonebuf; CHK_GWBUF(head); src_type = head->gwbuf_type; if (targettype == GWBUF_TYPE_UNDEFINED || src_type == GWBUF_TYPE_UNDEFINED || src_type == GWBUF_TYPE_PLAINSQL || targettype == src_type) { clonebuf = NULL; goto return_clonebuf; } if (GWBUF_IS_TYPE_MYSQL(head)) { if (GWBUF_TYPE_PLAINSQL == targettype) { /** Crete reference to string part of buffer */ clonebuf = gwbuf_clone_portion( head, 5, GWBUF_LENGTH(head)-5); ss_dassert(clonebuf != NULL); /** Overwrite the type with new format */ gwbuf_set_type(clonebuf, targettype); } else { clonebuf = NULL; } } else { clonebuf = NULL; } return_clonebuf: return clonebuf; }
static query_test_t* query_test_init( const char* query_str, skygw_query_type_t query_type, bool case_should_fail, bool exec_also_in_server) { query_test_t* qtest; qtest = (query_test_t *)calloc(1, sizeof(query_test_t)); ss_dassert(qtest != NULL); qtest->qt_chk_top = CHK_NUM_QUERY_TEST; qtest->qt_chk_tail = CHK_NUM_QUERY_TEST; qtest->qt_query_str = query_str; qtest->qt_query_type = query_type; qtest->qt_should_fail = case_should_fail; qtest->qt_exec_also_in_server = exec_also_in_server; return qtest; }
/** * gw_mysql_close * * close a connection if opened * free data scructure for MySQLProtocol * * @param ptr The MySQLProtocol ** to close/free * */ void gw_mysql_close(MySQLProtocol **ptr) { MySQLProtocol *conn = *ptr; ss_dassert(*ptr != NULL); if (*ptr == NULL) return; if (conn->fd > 0) { /* COM_QUIT will not be sent here, but from the caller of this routine! */ close(conn->fd); } else { // no socket here } free(*ptr); *ptr = NULL; }
/** * @node Unlink from backend server, unlink from router's connection list, * and free memory of a router client session. * * Parameters: * @param router - <usage> * <description> * * @param router_cli_ses - <usage> * <description> * * @return void * * * @details (write detailed description here) * */ static void freeSession( ROUTER* router_instance, void* router_client_ses) { ROUTER_INSTANCE* router = (ROUTER_INSTANCE *)router_instance; ROUTER_CLIENT_SES* router_cli_ses = (ROUTER_CLIENT_SES *)router_client_ses; int prev_val; prev_val = atomic_add(&router_cli_ses->backend->current_connection_count, -1); ss_dassert(prev_val > 0); spinlock_acquire(&router->lock); if (router->connections == router_cli_ses) { router->connections = router_cli_ses->next; } else { ROUTER_CLIENT_SES *ptr = router->connections; while (ptr != NULL && ptr->next != router_cli_ses) { ptr = ptr->next; } if (ptr != NULL) { ptr->next = router_cli_ses->next; } } spinlock_release(&router->lock); LOGIF(LD, (skygw_log_write_flush( LOGFILE_DEBUG, "%lu [freeSession] Unlinked router_client_session %p from " "router %p and from server on port %d. Connections : %d. ", pthread_self(), router_cli_ses, router, router_cli_ses->backend->server->port, prev_val-1))); free(router_cli_ses); }
/** * 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; }
bool gwbuf_set_type( GWBUF* buf, gwbuf_type_t type) { bool succp; CHK_GWBUF(buf); switch (type) { case GWBUF_TYPE_MYSQL: case GWBUF_TYPE_PLAINSQL: case GWBUF_TYPE_UNDEFINED: buf->gwbuf_type = type; succp = true; break; default: succp = false; break; } ss_dassert(succp); return succp; }
/** * Add a buffer object to GWBUF buffer. * * @param buf GWBUF where object is added * @param id Type identifier for object * @param data Object data * @param donefun_dp Clean-up function to be executed before buffer is freed. */ void gwbuf_add_buffer_object( GWBUF* buf, bufobj_id_t id, void* data, void (*donefun_fp)(void *)) { buffer_object_t** p_b; buffer_object_t* newb; CHK_GWBUF(buf); newb = (buffer_object_t *)malloc(sizeof(buffer_object_t)); ss_dassert(newb != NULL); if (newb == NULL) { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Memory allocation failed due to %s.", strerror(errno)))); return; } newb->bo_id = id; newb->bo_data = data; newb->bo_donefun_fp = donefun_fp; newb->bo_next = NULL; /** Lock */ spinlock_acquire(&buf->gwbuf_lock); p_b = &buf->gwbuf_bufobj; /** Search the end of the list and add there */ while (*p_b != NULL) { p_b = &(*p_b)->bo_next; } *p_b = newb; /** Set flag */ buf->gwbuf_info |= GWBUF_INFO_PARSED; /** Unlock */ spinlock_release(&buf->gwbuf_lock); }
/** * Add a property to a buffer. * * @param buf The buffer to add the property to * @param name The property name * @param value The property value * @return Non-zero on success */ int gwbuf_add_property(GWBUF *buf, char *name, char *value) { BUF_PROPERTY *prop; if ((prop = malloc(sizeof(BUF_PROPERTY))) == NULL) { ss_dassert(prop != NULL); LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Memory allocation failed due to %s.", strerror(errno)))); return 0; } prop->name = strdup(name); prop->value = strdup(value); spinlock_acquire(&buf->gwbuf_lock); prop->next = buf->properties; buf->properties = prop; spinlock_release(&buf->gwbuf_lock); return 1; }
static int handle_state_switch(DCB* dcb,DCB_REASON reason, void * routersession) { ss_dassert(dcb != NULL); SESSION* session = dcb->session; ROUTER_CLIENT_SES* rses = (ROUTER_CLIENT_SES*)routersession; SERVICE* service = session->service; ROUTER* router = (ROUTER *)service->router; switch(reason) { case DCB_REASON_CLOSE: dcb->func.close(dcb); break; case DCB_REASON_DRAINED: /** Do we need to do anything? */ break; case DCB_REASON_HIGH_WATER: /** Do we need to do anything? */ break; case DCB_REASON_LOW_WATER: /** Do we need to do anything? */ break; case DCB_REASON_ERROR: dcb->func.error(dcb); break; case DCB_REASON_HUP: dcb->func.hangup(dcb); break; case DCB_REASON_NOT_RESPONDING: dcb->func.hangup(dcb); break; default: break; } return 0; }
GWBUF *gwbuf_clone_portion( GWBUF *buf, size_t start_offset, size_t length) { GWBUF* clonebuf; CHK_GWBUF(buf); ss_dassert(start_offset+length <= GWBUF_LENGTH(buf)); if ((clonebuf = (GWBUF *)malloc(sizeof(GWBUF))) == NULL) { return NULL; } atomic_add(&buf->sbuf->refcount, 1); clonebuf->sbuf = buf->sbuf; clonebuf->start = (void *)((char*)buf->start)+start_offset; clonebuf->end = (void *)((char *)clonebuf->start)+length; clonebuf->gwbuf_type = buf->gwbuf_type; /*< clone the type for now */ clonebuf->next = NULL; CHK_GWBUF(clonebuf); return clonebuf; }
/** * 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; }
/** * secrets_readKeys * * This routine reads data from a binary file and extracts the AES encryption key * and the AES Init Vector * * @return The keys structure or NULL on error */ static MAXKEYS * secrets_readKeys() { char secret_file[255]; char *home; MAXKEYS *keys; struct stat secret_stats; int fd; int len; home = getenv("MAXSCALE_HOME"); if (home == NULL) { home = "/usr/local/skysql/MaxScale"; } snprintf(secret_file, 255, "%s/etc/.secrets", home); /* Try to access secrets file */ if (access(secret_file, R_OK) == -1) { int eno = errno; errno = 0; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : access for secrets file " "[%s] failed. Error %d, %s.", secret_file, eno, strerror(eno)))); return NULL; } /* open secret file */ if ((fd = open(secret_file, O_RDONLY)) < 0) { int eno = errno; errno = 0; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Failed opening secret " "file [%s]. Error %d, %s.", secret_file, eno, strerror(eno)))); return NULL; } /* accessing file details */ if (fstat(fd, &secret_stats) < 0) { int eno = errno; errno = 0; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : fstat for secret file %s " "failed. Error %d, %s.", secret_file, eno, strerror(eno)))); return NULL; } if (secret_stats.st_size != sizeof(MAXKEYS)) { int eno = errno; errno = 0; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Secrets file %s has " "incorrect size. Error %d, %s.", secret_file, eno, strerror(eno)))); return NULL; } if (secret_stats.st_mode != (S_IRUSR|S_IFREG)) { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Ignoring secrets file " "%s, invalid permissions.", secret_file))); return NULL; } if ((keys = (MAXKEYS *)malloc(sizeof(MAXKEYS))) == NULL) { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Memory allocation failed " "for key structure."))); return NULL; } /** * Read all data from file. * MAXKEYS (secrets.h) is struct for key, _not_ length-related macro. */ len = read(fd, keys, sizeof(MAXKEYS)); if (len != sizeof(MAXKEYS)) { int eno = errno; errno = 0; free(keys); LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Read from secrets file " "%s failed. Read %d, expected %d bytes. Error %d, %s.", secret_file, len, sizeof(MAXKEYS), eno, strerror(eno)))); return NULL; } /* Close the file */ if (close(fd) < 0) { int eno = errno; errno = 0; free(keys); LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Failed closing the " "secrets file %s. Error %d, %s.", secret_file, eno, strerror(eno)))); return NULL; } ss_dassert(keys != NULL); return keys; }
/** * Add a DCB to the set of descriptors within the polling * environment. * * @param dcb The descriptor to add to the poll * @return -1 on error or 0 on success */ int poll_add_dcb(DCB *dcb) { int rc = -1; dcb_state_t old_state = DCB_STATE_UNDEFINED; dcb_state_t new_state; struct epoll_event ev; CHK_DCB(dcb); #ifdef EPOLLRDHUP ev.events = EPOLLIN | EPOLLOUT | EPOLLRDHUP | EPOLLHUP | EPOLLET; #else ev.events = EPOLLIN | EPOLLOUT | EPOLLHUP | EPOLLET; #endif ev.data.ptr = dcb; /*< * Choose new state according to the role of dcb. */ if (dcb->dcb_role == DCB_ROLE_REQUEST_HANDLER) { new_state = DCB_STATE_POLLING; } else { ss_dassert(dcb->dcb_role == DCB_ROLE_SERVICE_LISTENER); new_state = DCB_STATE_LISTENING; } /*< * If dcb is in unexpected state, state change fails indicating that dcb * is not polling anymore. */ if (dcb_set_state(dcb, new_state, &old_state)) { rc = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, dcb->fd, &ev); if (rc != 0) { int eno = errno; errno = 0; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Adding dcb %p in state %s " "to poll set failed. epoll_ctl failed due " "%d, %s.", dcb, STRDCBSTATE(dcb->state), eno, strerror(eno)))); } else { LOGIF(LD, (skygw_log_write( LOGFILE_DEBUG, "%lu [poll_add_dcb] Added dcb %p in state %s to " "poll set.", pthread_self(), dcb, STRDCBSTATE(dcb->state)))); } ss_info_dassert(rc == 0, "Unable to add poll"); /*< trap in debug */ } else { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Unable to set new state for dcb %p " "in state %s. Adding to poll set failed.", dcb, STRDCBSTATE(dcb->state)))); } return rc; }
/** * Associate a new session with this instance of the filter. * * Create the file to log to and open it. * * @param instance The filter instance data * @param session The session itself * @return Session specific data for this session */ static void * newSession(FILTER *instance, SESSION *session) { TEE_INSTANCE *my_instance = (TEE_INSTANCE *) instance; TEE_SESSION *my_session; char *remote, *userName; if (strcmp(my_instance->service->name, session->service->name) == 0) { MXS_ERROR("%s: Recursive use of tee filter in service.", session->service->name); my_session = NULL; goto retblock; } HASHTABLE* ht = hashtable_alloc(100, simple_str_hash, strcmp); bool is_loop = detect_loops(my_instance, ht, session->service); hashtable_free(ht); if (is_loop) { MXS_ERROR("%s: Recursive use of tee filter in service.", session->service->name); my_session = NULL; goto retblock; } if ((my_session = calloc(1, sizeof(TEE_SESSION))) != NULL) { my_session->active = 1; my_session->residual = 0; my_session->tee_replybuf = NULL; my_session->client_dcb = session->client; my_session->instance = my_instance; my_session->client_multistatement = false; my_session->queue = NULL; spinlock_init(&my_session->tee_lock); if (my_instance->source && (remote = session_get_remote(session)) != NULL) { if (strcmp(remote, my_instance->source)) { my_session->active = 0; MXS_WARNING("Tee filter is not active."); } } userName = session_getUser(session); if (my_instance->userName && userName && strcmp(userName, my_instance->userName)) { my_session->active = 0; MXS_WARNING("Tee filter is not active."); } if (my_session->active) { DCB* dcb; SESSION* ses; FILTER_DEF* dummy; UPSTREAM* dummy_upstream; if ((dcb = dcb_clone(session->client)) == NULL) { freeSession(instance, (void *) my_session); my_session = NULL; MXS_ERROR("Creating client DCB for Tee " "filter failed. Terminating session."); goto retblock; } if ((dummy = filter_alloc("tee_dummy", "tee_dummy")) == NULL) { dcb_close(dcb); freeSession(instance, (void *) my_session); my_session = NULL; MXS_ERROR("tee: Allocating memory for " "dummy filter definition failed." " Terminating session."); goto retblock; } if ((ses = session_alloc(my_instance->service, dcb)) == NULL) { filter_free(dummy); dcb_close(dcb); freeSession(instance, (void *) my_session); my_session = NULL; MXS_ERROR("Creating client session for Tee " "filter failed. Terminating session."); goto retblock; } ss_dassert(ses->ses_is_child); dummy->obj = GetModuleObject(); dummy->filter = NULL; my_session->branch_session = ses; my_session->branch_dcb = dcb; my_session->dummy_filterdef = dummy; if ((dummy_upstream = filterUpstream( dummy, my_session, &ses->tail)) == NULL) { filter_free(dummy); closeSession(instance, (void*) my_session); dcb_close(dcb); free(my_session); MXS_ERROR("tee: Allocating memory for" "dummy upstream failed." " Terminating session."); return NULL; } ses->tail = *dummy_upstream; MySQLProtocol* protocol = (MySQLProtocol*) session->client->protocol; my_session->use_ok = protocol->client_capabilities & (1 << 6); free(dummy_upstream); } } retblock: return my_session; }
/** * This routine reads data from a binary file named ".secrets" and extracts the AES encryption key * and the AES Init Vector. * If the path parameter is not null the custom path is interpreted as a folder * containing the .secrets file. Otherwise the default location is used. * @return The keys structure or NULL on error */ static MAXKEYS * secrets_readKeys(char* path) { char secret_file[PATH_MAX+1]; char *home; MAXKEYS *keys; struct stat secret_stats; int fd; int len; static int reported = 0; if(path != NULL) snprintf(secret_file, PATH_MAX, "%s/.secrets", path); else snprintf(secret_file, PATH_MAX, "%s/.secrets", get_datadir()); /* Try to access secrets file */ if (access(secret_file, R_OK) == -1) { int eno = errno; errno = 0; if (eno == ENOENT) { if (!reported) { LOGIF(LM, (skygw_log_write( LOGFILE_MESSAGE, "Encrypted password file %s can't be accessed " "(%s). Password encryption is not used.", secret_file, strerror(eno)))); reported = 1; } } else { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : access for secrets file " "[%s] failed. Error %d, %s.", secret_file, eno, strerror(eno)))); } return NULL; } /* open secret file */ if ((fd = open(secret_file, O_RDONLY)) < 0) { int eno = errno; errno = 0; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Failed opening secret " "file [%s]. Error %d, %s.", secret_file, eno, strerror(eno)))); return NULL; } /* accessing file details */ if (fstat(fd, &secret_stats) < 0) { int eno = errno; errno = 0; close(fd); LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : fstat for secret file %s " "failed. Error %d, %s.", secret_file, eno, strerror(eno)))); return NULL; } if (secret_stats.st_size != sizeof(MAXKEYS)) { int eno = errno; errno = 0; close(fd); LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Secrets file %s has " "incorrect size. Error %d, %s.", secret_file, eno, strerror(eno)))); return NULL; } if (secret_stats.st_mode != (S_IRUSR|S_IFREG)) { close(fd); LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Ignoring secrets file " "%s, invalid permissions.", secret_file))); return NULL; } if ((keys = (MAXKEYS *)malloc(sizeof(MAXKEYS))) == NULL) { close(fd); LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Memory allocation failed " "for key structure."))); return NULL; } /** * Read all data from file. * MAXKEYS (secrets.h) is struct for key, _not_ length-related macro. */ len = read(fd, keys, sizeof(MAXKEYS)); if (len != sizeof(MAXKEYS)) { int eno = errno; errno = 0; close(fd); free(keys); LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Read from secrets file " "%s failed. Read %d, expected %d bytes. Error %d, %s.", secret_file, len, sizeof(MAXKEYS), eno, strerror(eno)))); return NULL; } /* Close the file */ if (close(fd) < 0) { int eno = errno; errno = 0; free(keys); LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Failed closing the " "secrets file %s. Error %d, %s.", secret_file, eno, strerror(eno)))); return NULL; } ss_dassert(keys != NULL); return keys; }
int main(int argc, char** argv) { slist_cursor_t* c; const char* q; query_test_t* qtest; bool succp; bool failp = true; unsigned int f = 0; int nsucc = 0; int nfail = 0; int rc = 0; MYSQL* mysql; char* workingdir; char ddoption[1024]; ss_dfprintf(stderr, ">> testmain\n"); c = slist_init(); /** Test some functions */ q = "SELECT MY_UDF('Hello')"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_WRITE, false, true)); /** This could be QUERY_TYPE_LOCAL_READ */ q = "SELECT repeat('a', 1024)"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_READ, false, true)); /** This could be QUERY_TYPE_LOCAL_READ */ q = "SELECT soundex('Hello')"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_READ, false, true)); q = "SELECT ssoundexx('Hello')"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_WRITE, false, true)); /** This could be QUERY_TYPE_LOCAL_READ */ q = "SELECT now()"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_READ, false, true)); /** This could be QUERY_TYPE_LOCAL_READ */ q = "SELECT rand()"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_READ, false, true)); q = "SELECT rand(234), MY_UDF('Hello'), soundex('Hello')"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_WRITE, false, true)); /** Read-only SELECTs */ q = "SELECT user from mysql.user"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_READ, false, true)); q = "select tt1.id, tt2.id from t1 tt1, t2 tt2 where tt1.name is " "not null and tt2.name is not null"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_READ, false, false)); /** SELECT ..INTO clauses > session updates */ q = "SELECT user from mysql.user INTO DUMPFILE '/tmp/dump1'"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_SESSION_WRITE, false, false)); q = "SELECT user INTO DUMPFILE '/tmp/dump2 ' from mysql.user"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_SESSION_WRITE, false, false)); q = "SELECT user from mysql.user INTO OUTFILE '/tmp/out1'"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_SESSION_WRITE, false, false)); /** Database and table name must be separated by a dot */ q = "SELECT user INTO OUTFILE '/tmp/out2 ' from mysql-user"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_SESSION_WRITE, true, false)); /** Database and table name must be separated by a dot */ q = "SELECT user INTO OUTFILE '/tmp/out2 ' from mysql_foo_user"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_SESSION_WRITE, false, false)); q = "SELECT user FROM mysql.user limit 1 INTO @local_variable"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_SESSION_WRITE, false, false)); q = "SELECT user INTO @local_variable FROM mysql.user limit 1"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_SESSION_WRITE, false, false)); q = "SELECT non_existent_attr INTO @d FROM non_existent_table"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_SESSION_WRITE, false, false)); q = "select * from table1 " "where table1.field IN " "(select * from table1a union select * from table1b) union " "select * from table2 where table2.field = " "(select (select f1 from table2a where table2a.f2 = table2b.f3) " "from table2b where table2b.f1 = table2.f2) union " "select * from table3"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_READ, false, true)); /** RENAME TABLEs */ q = "RENAME TABLE T1 to T2"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_WRITE, false, false)); /** INSERTs */ q = "INSERT INTO T1 (SELECT * FROM T2)"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_WRITE, false, true)); q = "INSERT INTO T1 VALUES(2, 'foo', 'toomanyattributes')"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_WRITE, false, true)); q = "INSERT INTO T2 VALUES(1, 'sthrgey')"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_WRITE, false, false)); q = "INSERT INTO T2 VALUES(8, 'ergstrhe')"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_WRITE, false, false)); q = "INSERT INTO T2 VALUES(9, NULL)"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_WRITE, false, false)); /** Ok, delimeter is client-side parameter which shouldn't be handled * on server side. */ q = "delimiter //"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_SESSION_WRITE, true, true)); /** SETs, USEs > Session updates */ q = "SET @a=1"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_SESSION_WRITE, false, true)); q = "USE TEST"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_SESSION_WRITE, false, false)); /** Object creation statements */ q = "create procedure si (out param1 int) \nbegin select count(*) " "into param1 from t1; \nend"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_WRITE, false, true)); q = "CREATE TABLE T1 (id integer primary key, name varchar(10))"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_WRITE, false, true)); q = "DROP TABLE T1"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_WRITE, false, false)); q = "ALTER TABLE T1 ADD COLUMN WHYME INTEGER NOT NULL"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_WRITE, false, false)); q = "TRUNCATE TABLE T1"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_WRITE, false, false)); q = "DROP SERVER IF EXISTS VICTIMSRV"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_SESSION_WRITE, false, true)); q = "CREATE USER FOO IDENTIFIED BY 'BAR'"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_WRITE, false, true)); q = "OPTIMIZE NO_WRITE_TO_BINLOG TABLE T1"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_WRITE, false, true)); q = "SELECT NOW();CREATE TABLE T1 (ID INTEGER);" "SET sql_log_bin=0;CREATE TABLE T2 (ID INTEGER)"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_WRITE, false, true)); /** Setting database makes this SESSION_WRITE */ q = "USE TEST;CREATE TABLE T1 (ID INTEGER);" "SET sql_log_bin=0;CREATE TABLE T2 (ID INTEGER)"; slcursor_add_case( c, query_test_init(q, QUERY_TYPE_SESSION_WRITE, false, true)); /** * Init libmysqld. */ workingdir = getenv("PWD"); if (workingdir == NULL) { fprintf(stderr, "Failed to resolve the working directory, $PWD is not " "set.\n"); ss_dassert(workingdir != NULL); } else if (access(workingdir, R_OK) != 0) { char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "Failed to access the working directory due %d, %s\n", errno, strerror_r(errno, errbuf, sizeof(errbuf))); ss_dassert(false); } else { char** so = server_options; snprintf(datadir, 1023, "%s/data", workingdir); mkdir(datadir, 0777); snprintf(ddoption, 1023, "--datadir=%s", datadir); while (strncmp(*so++, "--datadir=", 10) != 0) ; if (*so == NULL) { fprintf(stderr, "Failed to find datadir option.\n"); ss_dassert(*so != NULL); } *so = ddoption; snprintf(mysqldir, 1023, "%s/mysql", workingdir); setenv("MYSQL_HOME", mysqldir, 1); } failp = mysql_library_init(num_elements, server_options, server_groups); if (failp) { MYSQL* mysql = mysql_init(NULL); ss_dassert(mysql != NULL); fprintf(stderr, "mysql_init failed, %d : %s\n", mysql_errno(mysql), mysql_error(mysql)); ss_dassert(!failp); } fprintf(stderr, "\nExecuting selected cases in " "skygw_query_classifier_get_type :\n\n"); /** * Set cursor to the beginning, scan through the list and execute * test cases. */ succp = slcursor_move_to_begin(c); while(succp) { qtest = slcursor_get_case(c); qtest->qt_result_type = skygw_query_classifier_get_type(qtest->qt_query_str, f, &mysql); succp = slcursor_step_ahead(c); } /** * Scan through test results and compare them against expected * results. */ succp = slcursor_move_to_begin(c); fprintf(stderr, "\nScanning through the results :\n\n"); while(succp) { qtest = slcursor_get_case(c); if (!query_test_types_match(qtest)) { nfail += 1; ss_dfprintf(stderr, "* Failed: \"%s\" -> %s (Expected %s)\n", query_test_get_querystr(qtest), STRQTYPE(query_test_get_result_type(qtest)), STRQTYPE(query_test_get_query_type(qtest))); } else { nsucc += 1; ss_dfprintf(stderr, "Succeed\t: \"%s\" -> %s\n", query_test_get_querystr(qtest), STRQTYPE(query_test_get_query_type(qtest))); } succp = slcursor_step_ahead(c); } fprintf(stderr, "------------------------------------------\n" "Tests in total %d, SUCCEED %d, FAILED %d\n", nsucc+nfail, nsucc, nfail); /** * Scan test results and re-execute those which are marked to be * executed also in the server. This serves mostly debugging purposes. */ succp = slcursor_move_to_begin(c); mysql = mysql_init(NULL); if (mysql == NULL) { fprintf(stderr, "mysql_init failed\n"); ss_dassert(mysql != NULL); } mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, "libmysqld_client"); mysql_options(mysql, MYSQL_OPT_USE_EMBEDDED_CONNECTION, NULL); mysql_options(mysql, MYSQL_OPT_USE_EMBEDDED_CONNECTION, NULL); mysql = mysql_real_connect(mysql, NULL, "skygw", "skygw", NULL, 0, NULL, CLIENT_MULTI_STATEMENTS); if (mysql == NULL) { fprintf(stderr, "mysql_real_connect failed\n"); ss_dassert(mysql != NULL); } fprintf(stderr, "\nRe-execution of selected cases in Embedded server :\n\n"); while(succp) { qtest = slcursor_get_case(c); if (query_test_exec_also_in_server(qtest)) { MYSQL_RES* results; MYSQL_ROW record; const char* query_str; query_str = query_test_get_querystr(qtest); failp = mysql_query(mysql, query_str); if (failp) { ss_dfprintf(stderr, "* Failed: \"%s\" -> %d : %s\n", query_str, mysql_errno(mysql), mysql_error(mysql)); } else { ss_dfprintf(stderr, "Succeed\t: \"%s\"\n", query_str); results = mysql_store_result(mysql); if (results != NULL) { while((record = mysql_fetch_row(results))) { while(record != NULL && *record != NULL) { ss_dfprintf(stderr, "%s ", *record); record++; } ss_dfprintf(stderr, "\n"); } mysql_free_result(results); } } } succp = slcursor_step_ahead(c); } slist_done(c); fprintf(stderr, "------------------------------------------\n"); return_with_handle: mysql_close(mysql); mysql_thread_end(); mysql_library_end(); return_without_server: ss_dfprintf(stderr, "\n<< testmain\n"); fflush(stderr); return rc; }