diff --git a/server/core/config.c b/server/core/config.c index 4da5135e7..dad4c4022 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -29,7 +29,8 @@ * 06/02/14 Massimiliano Pinto Added support for enable/disable root user in services * 14/02/14 Massimiliano Pinto Added enable_root_user in the service_params list * 11/03/14 Massimiliano Pinto Added Unix socket support - * 11/05/14 Massimiliano Pinto Added version_string support to service + * 11/05/14 Massimiliano Pinto Added version_string support to service + * 19/05/14 Mark Riddoch Added unique names from section headers * * @endverbatim */ @@ -299,6 +300,7 @@ int error_count = 0; obj->element = server_alloc(address, protocol, atoi(port)); + server_set_unique_name(obj->element, obj->object); } else { diff --git a/server/core/dbusers.c b/server/core/dbusers.c index e5289a3a0..da079b3f7 100644 --- a/server/core/dbusers.c +++ b/server/core/dbusers.c @@ -183,6 +183,8 @@ getUsers(SERVICE *service, struct users *users) } serviceGetUser(service, &service_user, &service_passwd); + if (service_user == NULL || service_passwd == NULL) + return -1; /** multi-thread environment requires that thread init succeeds. */ if (mysql_thread_init()) { diff --git a/server/core/dcb.c b/server/core/dcb.c index bfc815dfe..cd995bd23 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -47,6 +47,7 @@ * error and 0 bytes to read. * This fixes a bug with many reads from * backend + * 07/05/2014 Mark Riddoch Addition of callback mechanism * * @endverbatim */ @@ -80,6 +81,7 @@ static bool dcb_set_state_nomutex( DCB* dcb, const dcb_state_t new_state, dcb_state_t* old_state); +static void dcb_call_callback(DCB *dcb, DCB_REASON reason); DCB* dcb_get_zombies(void) { @@ -94,8 +96,8 @@ DCB* dcb_get_zombies(void) * * @return A newly allocated DCB or NULL if non could be allocated. */ -DCB * dcb_alloc( - dcb_role_t role) +DCB * +dcb_alloc(dcb_role_t role) { DCB *rval; @@ -119,11 +121,16 @@ DCB *rval; spinlock_init(&rval->delayqlock); spinlock_init(&rval->dcb_readqlock); spinlock_init(&rval->authlock); + spinlock_init(&rval->cb_lock); rval->fd = -1; memset(&rval->stats, 0, sizeof(DCBSTATS)); // Zero the statistics rval->state = DCB_STATE_ALLOC; bitmask_init(&rval->memdata.bitmask); + rval->writeqlen = 0; + rval->high_water = 0; + rval->low_water = 0; rval->next = NULL; + rval->callbacks = NULL; spinlock_acquire(&dcbspin); if (allDCBs == NULL) @@ -249,6 +256,8 @@ dcb_add_to_zombieslist(DCB *dcb) static void dcb_final_free(DCB *dcb) { +DCB_CALLBACK *cb; + CHK_DCB(dcb); ss_info_dassert(dcb->state == DCB_STATE_DISCONNECTED, "dcb not in DCB_STATE_DISCONNECTED state."); @@ -303,17 +312,27 @@ dcb_final_free(DCB *dcb) if (dcb->remote) free(dcb->remote); + /* Clear write and read buffers */ + if (dcb->delayq) { GWBUF *queue = dcb->delayq; while ((queue = gwbuf_consume(queue, GWBUF_LENGTH(queue))) != NULL); } - if (dcb->dcb_readqueue) { GWBUF* queue = dcb->dcb_readqueue; while ((queue = gwbuf_consume(queue, GWBUF_LENGTH(queue))) != NULL); } + + spinlock_acquire(&dcb->cb_lock); + while ((cb = dcb->callbacks) != NULL) + { + dcb->callbacks = cb->next; + free(cb); + } + spinlock_release(&dcb->cb_lock); + bitmask_free(&dcb->memdata.bitmask); simple_mutex_done(&dcb->dcb_read_lock); simple_mutex_done(&dcb->dcb_write_lock); @@ -689,9 +708,11 @@ return_n: int dcb_write(DCB *dcb, GWBUF *queue) { - int w; - int saved_errno = 0; +int w, qlen; +int saved_errno = 0; +int below_water; + below_water = (dcb->high_water && dcb->writeqlen < dcb->high_water) ? 1 : 0; ss_dassert(queue != NULL); if (queue == NULL || @@ -725,6 +746,8 @@ dcb_write(DCB *dcb, GWBUF *queue) * the routine that drains the queue data, so we should * not have a race condition on the event. */ + qlen = gwbuf_length(queue); + atomic_add(&dcb->writeqlen, qlen); dcb->writeq = gwbuf_append(dcb->writeq, queue); dcb->stats.n_buffered++; LOGIF(LD, (skygw_log_write( @@ -837,6 +860,8 @@ dcb_write(DCB *dcb, GWBUF *queue) * for suspended write. */ dcb->writeq = queue; + qlen = gwbuf_length(queue); + atomic_add(&dcb->writeqlen, qlen); if (queue != NULL) { @@ -860,6 +885,13 @@ dcb_write(DCB *dcb, GWBUF *queue) return 0; } spinlock_release(&dcb->writeqlock); + + if (dcb->high_water && dcb->writeqlen > dcb->high_water && below_water) + { + atomic_add(&dcb->stats.n_high_water, 1); + dcb_call_callback(dcb, DCB_REASON_HIGH_WATER); + } + return 1; } @@ -874,9 +906,12 @@ dcb_write(DCB *dcb, GWBUF *queue) int dcb_drain_writeq(DCB *dcb) { -int n = 0; -int w; -int saved_errno = 0; +int n = 0; +int w; +int saved_errno = 0; +int above_water; + + above_water = (dcb->low_water && dcb->writeqlen > dcb->low_water) ? 1 : 0; spinlock_acquire(&dcb->writeqlock); if (dcb->writeq) @@ -937,6 +972,17 @@ int saved_errno = 0; } } spinlock_release(&dcb->writeqlock); + atomic_add(&dcb->writeqlen, -n); + /* The write queue has drained, potentially need to call a callback function */ + if (dcb->writeq == NULL) + dcb_call_callback(dcb, DCB_REASON_DRAINED); + if (above_water && dcb->writeqlen < dcb->low_water) + { + atomic_add(&dcb->stats.n_low_water, 1); + dcb_call_callback(dcb, DCB_REASON_LOW_WATER); + } + + return n; } @@ -979,6 +1025,8 @@ dcb_close(DCB *dcb) ss_dassert(dcb->state == DCB_STATE_NOPOLLING || dcb->state == DCB_STATE_ZOMBIE); + dcb_call_callback(dcb, DCB_REASON_CLOSE); + if (rc == 0) { LOGIF(LD, (skygw_log_write( LOGFILE_DEBUG, @@ -1021,6 +1069,8 @@ printDCB(DCB *dcb) printf("\t\tNo. of Writes: %d\n", dcb->stats.n_writes); printf("\t\tNo. of Buffered Writes: %d\n", dcb->stats.n_buffered); printf("\t\tNo. of Accepts: %d\n", dcb->stats.n_accepts); + printf("\t\tNo. of High Water Events: %d\n", dcb->stats.n_high_water); + printf("\t\tNo. of Low Water Events: %d\n", dcb->stats.n_low_water); } /** @@ -1067,6 +1117,8 @@ DCB *dcb; dcb_printf(pdcb, "\t\tNo. of Writes: %d\n", dcb->stats.n_writes); dcb_printf(pdcb, "\t\tNo. of Buffered Writes: %d\n", dcb->stats.n_buffered); dcb_printf(pdcb, "\t\tNo. of Accepts: %d\n", dcb->stats.n_accepts); + dcb_printf(pdcb, "\t\tNo. of High Water Events: %d\n", dcb->stats.n_high_water); + dcb_printf(pdcb, "\t\tNo. of Low Water Events: %d\n", dcb->stats.n_low_water); dcb = dcb->next; } spinlock_release(&dcbspin); @@ -1092,6 +1144,8 @@ dprintDCB(DCB *pdcb, DCB *dcb) dcb_printf(pdcb, "\t\tNo. of Writes: %d\n", dcb->stats.n_writes); dcb_printf(pdcb, "\t\tNo. of Buffered Writes: %d\n", dcb->stats.n_buffered); dcb_printf(pdcb, "\t\tNo. of Accepts: %d\n", dcb->stats.n_accepts); + dcb_printf(pdcb, "\t\tNo. of High Water Events: %d\n", dcb->stats.n_high_water); + dcb_printf(pdcb, "\t\tNo. of Low Water Events: %d\n", dcb->stats.n_low_water); } /** @@ -1428,4 +1482,163 @@ int gw_write( return w; } +/** + * Add a callback + * + * Duplicate registrations are not allowed, therefore an error will be returned if + * the specific function, reason and userdata triple are already registered. + * An error will also be returned if the is insufficient memeory available to + * create the registration. + * + * @param dcb The DCB to add the callback to + * @param reason The callback reason + * @param cb The callback function to call + * @param userdata User data to send in the call + * @return Non-zero (true) if the callback was added + */ +int +dcb_add_callback(DCB *dcb, DCB_REASON reason, int (*callback)(struct dcb *, DCB_REASON, void *), void *userdata) +{ +DCB_CALLBACK *cb, *ptr; +int rval = 1; + if ((ptr = (DCB_CALLBACK *)malloc(sizeof(DCB_CALLBACK))) == NULL) + { + return 0; + } + ptr->reason = reason; + ptr->cb = callback; + ptr->userdata = userdata; + ptr->next = NULL; + spinlock_acquire(&dcb->cb_lock); + cb = dcb->callbacks; + if (cb == NULL) + { + dcb->callbacks = ptr; + spinlock_release(&dcb->cb_lock); + } + else + { + while (cb) + { + if (cb->reason == reason && cb->cb == callback && + cb->userdata == userdata) + { + free(ptr); + spinlock_release(&dcb->cb_lock); + return 0; + } + if (cb->next == NULL) + cb->next = ptr; + cb = cb->next; + } + spinlock_release(&dcb->cb_lock); + } + return rval; +} + +/** + * Remove a callback from the callback list for the DCB + * + * Searches down the linked list to find he callback with a matching reason, function + * and userdata. + * + * @param dcb The DCB to add the callback to + * @param reason The callback reason + * @param cb The callback function to call + * @param userdata User data to send in the call + * @return Non-zero (true) if the callback was removed + */ +int +dcb_remove_callback(DCB *dcb, DCB_REASON reason, int (*callback)(struct dcb *, DCB_REASON), void *userdata) +{ +DCB_CALLBACK *cb, *pcb = NULL; +int rval = 0; + + spinlock_acquire(&dcb->cb_lock); + cb = dcb->callbacks; + if (cb == NULL) + { + rval = 0; + } + else + { + while (cb) + { + if (cb->reason == reason && cb->cb == callback + && cb->userdata == userdata) + { + if (pcb == NULL) + pcb->next = cb->next; + else + dcb->callbacks = cb->next; + spinlock_release(&dcb->cb_lock); + free(cb); + rval = 1; + break; + } + pcb = cb; + cb = cb->next; + } + } + if (!rval) + spinlock_release(&dcb->cb_lock); + return rval; +} + +/** + * Call the set of callbacks registered for a particular reason. + * + * @param dcb The DCB to call the callbacks regarding + * @param reason The reason that has triggered the call + */ +static void +dcb_call_callback(DCB *dcb, DCB_REASON reason) +{ +DCB_CALLBACK *cb, *nextcb; + + spinlock_acquire(&dcb->cb_lock); + cb = dcb->callbacks; + while (cb) + { + if (cb->reason == reason) + { + nextcb = cb->next; + spinlock_release(&dcb->cb_lock); + cb->cb(dcb, reason, cb->userdata); + spinlock_acquire(&dcb->cb_lock); + cb = nextcb; + } + else + cb = cb->next; + } + spinlock_release(&dcb->cb_lock); +} + +/** + * Check the passed DCB to ensure it is in the list of allDCBS + * + * @param DCB The DCB to check + * @return 1 if the DCB is in the list, otherwise 0 + */ +int +dcb_isvalid(DCB *dcb) +{ +DCB *ptr; +int rval = 0; + + spinlock_acquire(&dcbspin); + ptr = allDCBs; + while (ptr) + { + if (ptr == dcb) + { + rval = 1; + break; + } + ptr = ptr->next; + } + spinlock_release(&dcbspin); + + return rval; +} diff --git a/server/core/monitor.c b/server/core/monitor.c index 6c028cad6..cee2f2d9e 100644 --- a/server/core/monitor.c +++ b/server/core/monitor.c @@ -197,3 +197,26 @@ MONITOR *ptr; } spinlock_release(&monLock); } + +/** + * Find a monitor by name + * + * @param name The name of the monitor + * @return Pointer to the monitor or NULL + */ +MONITOR * +monitor_find(char *name) +{ +MONITOR *ptr; + + spinlock_acquire(&monLock); + ptr = allMonitors; + while (ptr) + { + if (!strcmp(ptr->name, name)) + break; + ptr = ptr->next; + } + spinlock_release(&monLock); + return ptr; +} diff --git a/server/core/server.c b/server/core/server.c index bf5b3174f..fd3e1c876 100644 --- a/server/core/server.c +++ b/server/core/server.c @@ -67,6 +67,7 @@ SERVER *server; server->nextdb = NULL; server->monuser = NULL; server->monpw = NULL; + server->unique_name = NULL; spinlock_acquire(&server_spin); server->next = allServers; @@ -109,10 +110,49 @@ SERVER *ptr; /* Clean up session and free the memory */ free(server->name); free(server->protocol); + if (server->unique_name) + free(server->unique_name); free(server); return 1; } +/** + * Set a unique name for the server + * + * @param server The server to ste the name on + * @param name The unique name for the server + */ +void +server_set_unique_name(SERVER *server, char *name) +{ + server->unique_name = strdup(name); +} + +/** + * Find an existing server using the unique section name in + * configuration file + * + * @param servname The Server name or address + * @param port The server port + * @return The server or NULL if not found + */ +SERVER * +server_find_by_unique_name(char *name) +{ +SERVER *server; + + spinlock_acquire(&server_spin); + server = allServers; + while (server) + { + if (strcmp(server->unique_name, name) == 0) + break; + server = server->next; + } + spinlock_release(&server_spin); + return server; +} + /** * Find an existing server * @@ -190,7 +230,7 @@ char *stat; ptr = allServers; while (ptr) { - dcb_printf(dcb, "Server %p\n", ptr); + dcb_printf(dcb, "Server %p (%s)\n", ptr, ptr->unique_name); dcb_printf(dcb, "\tServer: %s\n", ptr->name); stat = server_status(ptr); dcb_printf(dcb, "\tStatus: %s\n", stat); @@ -215,7 +255,7 @@ dprintServer(DCB *dcb, SERVER *server) { char *stat; - dcb_printf(dcb, "Server %p\n", server); + dcb_printf(dcb, "Server %p (%s)\n", server, server->unique_name); dcb_printf(dcb, "\tServer: %s\n", server->name); stat = server_status(server); dcb_printf(dcb, "\tStatus: %s\n", stat); diff --git a/server/core/service.c b/server/core/service.c index e44b9c697..93db01c83 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -29,6 +29,7 @@ * 25/02/14 Massimiliano Pinto Added: service refresh limit feature * 28/02/14 Massimiliano Pinto users_alloc moved from service_alloc to serviceStartPort (generic hashable for services) * 07/05/14 Massimiliano Pinto Added: version_string initialized to NULL + * 23/05/14 Mark Riddoch Addition of service validation call * * @endverbatim */ @@ -114,6 +115,33 @@ SERVICE *service; return service; } +/** + * Check to see if a service pointer is valid + * + * @param service The poitner to check + * @return 1 if the service is in the list of all services + */ +int +service_isvalid(SERVICE *service) +{ +SERVICE *ptr; +int rval = 0; + + spinlock_acquire(&service_spin); + ptr = allServices; + while (ptr) + { + if (ptr == service) + { + rval = 1; + break; + } + ptr = ptr->next; + } + spinlock_release(&service_spin); + return rval; +} + /** * Start an individual port/protocol pair * diff --git a/server/core/session.c b/server/core/session.c index 4df4cef07..3e080c4ca 100644 --- a/server/core/session.c +++ b/server/core/session.c @@ -114,7 +114,7 @@ session_alloc(SERVICE *service, DCB *client_dcb) /* * Only create a router session if we are not the listening - * DCB. Creating a router session may create a connection to a + * 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 * @@ -122,7 +122,7 @@ session_alloc(SERVICE *service, DCB *client_dcb) * session, therefore it is important that the session lock is * relinquished beforethe 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, @@ -269,6 +269,34 @@ return_succp : return succp; } +/** + * 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 + */ +int +session_isvalid(SESSION *session) +{ +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); + + return rval; +} + /** * Print details of an individual session * diff --git a/server/include/dcb.h b/server/include/dcb.h index f0c4cff31..9b05d9eaf 100644 --- a/server/include/dcb.h +++ b/server/include/dcb.h @@ -48,6 +48,8 @@ struct service; * 15/07/2013 Massimiliano Pinto Added session entry point * 16/07/2013 Massimiliano Pinto Added command type for dcb * 07/02/2014 Massimiliano Pinto Added ipv4 data struct into for dcb + * 07/05/2014 Mark Riddoch Addition of callback mechanism + * 08/05/2014 Mark Riddoch Addition of writeq high and low watermarks * * @endverbatim */ @@ -99,6 +101,8 @@ typedef struct dcbstats { int n_writes; /*< Number of writes on this descriptor */ int n_accepts; /*< Number of accepts on this descriptor */ int n_buffered; /*< Number of buffered writes */ + int n_high_water; /*< Number of crosses of high water mark */ + int n_low_water; /*< Number of crosses of low water mark */ } DCBSTATS; /** @@ -137,10 +141,35 @@ typedef enum { } dcb_state_t; typedef enum { - DCB_ROLE_SERVICE_LISTENER, /*< Receives initial connect requests from clients */ - DCB_ROLE_REQUEST_HANDLER /*< Serves dedicated client */ + DCB_ROLE_SERVICE_LISTENER, /*< Receives initial connect requests from clients */ + DCB_ROLE_REQUEST_HANDLER, /*< Serves dedicated client */ + DCB_ROLE_INTERNAL /*< Internal DCB not connected to the outside */ } dcb_role_t; +/** + * Callback reasons for the DCB callback mechanism. + */ +typedef enum { + DCB_REASON_CLOSE, /*< The DCB is closing */ + DCB_REASON_DRAINED, /*< The write delay queue has drained */ + DCB_REASON_HIGH_WATER, /*< Cross high water mark */ + DCB_REASON_LOW_WATER, /*< Cross low water mark */ + DCB_REASON_ERROR, /*< An error was flagged on the connection */ + DCB_REASON_HUP /*< A hangup was detected */ +} DCB_REASON; + +/** + * Callback structure - used to track callbacks registered on a DCB + */ +typedef struct dcb_callback { + DCB_REASON reason; /*< The reason for the callback */ + int (*cb)(struct dcb *dcb, DCB_REASON reason, void *userdata); + void *userdata; /*< User data to be sent in the callback */ + struct dcb_callback + *next; /*< Next callback for this DCB */ +} DCB_CALLBACK; + + /** * Descriptor Control Block * @@ -172,6 +201,7 @@ typedef struct dcb { struct session *session; /**< The owning session */ GWPROTOCOL func; /**< The functions for this descriptor */ + unsigned int writeqlen; /**< Current number of byes in the write queue */ SPINLOCK writeqlock; /**< Write Queue spinlock */ GWBUF *writeq; /**< Write Data Queue */ SPINLOCK delayqlock; /**< Delay Backend Write Queue spinlock */ @@ -187,6 +217,11 @@ typedef struct dcb { void *data; /**< Specific client data */ DCBMM memdata; /**< The data related to DCB memory management */ int command; /**< Specific client command type */ + SPINLOCK cb_lock; /**< The lock for the callbacks linked list */ + DCB_CALLBACK *callbacks; /**< The list of callbacks for the DCB */ + + unsigned int high_water; /**< High water mark */ + unsigned int low_water; /**< Low water mark */ #if defined(SS_DEBUG) skygw_chk_t dcb_chk_tail; #endif @@ -205,6 +240,11 @@ int fail_accept_errno; #define DCB_SESSION(x) (x)->session #define DCB_PROTOCOL(x, type) (type *)((x)->protocol) #define DCB_ISZOMBIE(x) ((x)->state == DCB_STATE_ZOMBIE) +#define DCB_WRITEQLEN(x) (x)->writeqlen +#define DCB_SET_LOW_WATER(x, lo) (x)->low_water = (lo); +#define DCB_SET_HIGH_WATER(x, hi) (x)->low_water = (hi); +#define DCB_BELOW_LOW_WATER(x) ((x)->low_water && (x)->writeqlen < (x)->low_water) +#define DCB_ABOVE_HIGH_WATER(x) ((x)->high_water && (x)->writeqlen > (x)->high_water) DCB *dcb_get_zombies(void); int gw_write( @@ -231,6 +271,11 @@ void dcb_printf(DCB *, const char *, ...); /* DCB version of printf */ int dcb_isclient(DCB *); /* the DCB is the client of the session */ void dcb_hashtable_stats(DCB *, void *); /**< Print statisitics */ void dcb_add_to_zombieslist(DCB* dcb); +int dcb_add_callback(DCB *, DCB_REASON, int (*)(struct dcb *, DCB_REASON, void *), + void *); +int dcb_remove_callback(DCB *, DCB_REASON, int (*)(struct dcb *, DCB_REASON), + void *); +int dcb_isvalid(DCB *); /* Check the DCB is in the linked list */ bool dcb_set_state( DCB* dcb, diff --git a/server/include/monitor.h b/server/include/monitor.h index 6aa0f2430..6444fecd5 100644 --- a/server/include/monitor.h +++ b/server/include/monitor.h @@ -29,6 +29,7 @@ * Date Who Description * 07/07/13 Mark Riddoch Initial implementation * 25/07/13 Mark Riddoch Addition of diagnotics + * 23/05/14 Mark Riddoch Addition of routine to find monitors by name * * @endverbatim */ @@ -79,6 +80,7 @@ typedef struct monitor { extern MONITOR *monitor_alloc(char *, char *); extern void monitor_free(MONITOR *); +extern MONITOR *monitor_find(char *); extern void monitorAddServer(MONITOR *, SERVER *); extern void monitorAddUser(MONITOR *, char *, char *); extern void monitorStop(MONITOR *); diff --git a/server/include/server.h b/server/include/server.h index e355e8c49..6b4598c36 100644 --- a/server/include/server.h +++ b/server/include/server.h @@ -51,6 +51,7 @@ typedef struct { * between the gateway and the server. */ typedef struct server { + char *unique_name; /**< Unique name for the server */ char *name; /**< Server name/IP address*/ unsigned short port; /**< Port to listen on */ char *protocol; /**< Protocol module to use */ @@ -103,6 +104,7 @@ typedef struct server { extern SERVER *server_alloc(char *, char *, unsigned short); extern int server_free(SERVER *); +extern SERVER *server_find_by_unique_name(char *); extern SERVER *server_find(char *, unsigned short); extern void printServer(SERVER *); extern void printAllServers(); diff --git a/server/include/service.h b/server/include/service.h index 85fe3972d..c7b9126f0 100644 --- a/server/include/service.h +++ b/server/include/service.h @@ -129,6 +129,7 @@ typedef struct service { extern SERVICE *service_alloc(char *, char *); extern int service_free(SERVICE *); extern SERVICE *service_find(char *); +extern int service_isvalid(SERVICE *); extern int serviceAddProtocol(SERVICE *, char *, char *, unsigned short); extern int serviceHasProtocol(SERVICE *, char *, unsigned short); extern void serviceAddBackend(SERVICE *, SERVER *); diff --git a/server/include/session.h b/server/include/session.h index 6cafa160d..c9f9f673c 100644 --- a/server/include/session.h +++ b/server/include/session.h @@ -87,6 +87,7 @@ typedef struct session { SESSION *session_alloc(struct service *, struct dcb *); bool session_free(SESSION *); +int session_isvalid(SESSION *); void printAllSessions(); void printSession(SESSION *); void dprintAllSessions(struct dcb *); diff --git a/server/modules/include/debugcli.h b/server/modules/include/debugcli.h index 0e7afcfe2..b373cae6b 100644 --- a/server/modules/include/debugcli.h +++ b/server/modules/include/debugcli.h @@ -41,6 +41,7 @@ struct cli_session; typedef struct cli_instance { SPINLOCK lock; /*< The instance spinlock */ SERVICE *service; /*< The debug cli service */ + int mode; /*< CLI interface mode */ struct cli_session *sessions; /*< Linked list of sessions within this instance */ struct cli_instance @@ -53,8 +54,13 @@ typedef struct cli_instance { */ typedef struct cli_session { char cmdbuf[80]; /*< The command buffer used to build up user commands */ + int mode; /*< The CLI Mode for this session */ SESSION *session; /*< The gateway session */ struct cli_session *next; /*< The next pointer for the list of sessions */ } CLI_SESSION; + +/* Command line interface modes */ +#define CLIM_USER 1 +#define CLIM_DEVELOPER 2 #endif diff --git a/server/modules/protocol/mysql_backend.c b/server/modules/protocol/mysql_backend.c index acc2dacdd..f1774485d 100644 --- a/server/modules/protocol/mysql_backend.c +++ b/server/modules/protocol/mysql_backend.c @@ -414,21 +414,23 @@ static int gw_read_backend_event(DCB *dcb) { if (dcb->session->client != NULL) { client_protocol = SESSION_PROTOCOL(dcb->session, MySQLProtocol); - } - - if (client_protocol != NULL) { - CHK_PROTOCOL(client_protocol); + if (client_protocol != NULL) { + CHK_PROTOCOL(client_protocol); - if (client_protocol->state == MYSQL_IDLE) - { - router->clientReply(router_instance, + if (client_protocol->state == MYSQL_IDLE) + { + router->clientReply(router_instance, rsession, writebuf, dcb); - rc = 1; - } - goto return_rc; - } + rc = 1; + } + goto return_rc; + } else if (dcb->session->client->dcb_role == DCB_ROLE_INTERNAL) { + router->clientReply(router_instance, rsession, writebuf, dcb); + rc = 1; + } + } } return_rc: diff --git a/server/modules/routing/Makefile b/server/modules/routing/Makefile index 1e50f3455..040c01bd1 100644 --- a/server/modules/routing/Makefile +++ b/server/modules/routing/Makefile @@ -49,6 +49,8 @@ MODULES= libdebugcli.so libreadconnroute.so libtestroute.so all: $(MODULES) + (cd readwritesplit; make ) + (cd binlog; make ) libtestroute.so: $(TESTOBJ) $(CC) $(LDFLAGS) $(TESTOBJ) $(LIBS) -o $@ @@ -68,19 +70,26 @@ libreadwritesplit.so: clean: rm -f $(OBJ) $(MODULES) (cd readwritesplit; touch depend.mk; make clean) + (cd binlog; touch depend.mk; make clean) tags: ctags $(SRCS) $(HDRS) (cd readwritesplit; make tags) + (cd binlog; make tags) depend: @rm -f depend.mk cc -M $(CFLAGS) $(SRCS) > depend.mk (cd readwritesplit; touch depend.mk ; make depend) + (cd binlog; touch depend.mk ; make depend) install: $(MODULES) install -D $(MODULES) $(DEST)/MaxScale/modules (cd readwritesplit; make DEST=$(DEST) install) +<<<<<<< HEAD +======= + (cd binlog; make DEST=$(DEST) install) +>>>>>>> blr cleantests: $(MAKE) -C readwritesplit/test cleantests diff --git a/server/modules/routing/debugcli.c b/server/modules/routing/debugcli.c index 66c02c98b..d6becdfda 100644 --- a/server/modules/routing/debugcli.c +++ b/server/modules/routing/debugcli.c @@ -45,7 +45,7 @@ extern int lm_enabled_logfiles_bitmask; -static char *version_str = "V1.0.1"; +static char *version_str = "V1.1.0"; /* The router entry points */ static ROUTER *createInstance(SERVICE *service, char **options); @@ -127,6 +127,7 @@ static ROUTER * createInstance(SERVICE *service, char **options) { CLI_INSTANCE *inst; +int i; if ((inst = malloc(sizeof(CLI_INSTANCE))) == NULL) return NULL; @@ -134,7 +135,29 @@ CLI_INSTANCE *inst; inst->service = service; spinlock_init(&inst->lock); inst->sessions = NULL; + inst->mode = CLIM_USER; + if (options) + { + for (i = 0; options[i]; i++) + { + if (!strcasecmp(options[i], "developer")) + { + inst->mode = CLIM_DEVELOPER; + } + else if (!strcasecmp(options[i], "user")) + { + inst->mode = CLIM_USER; + } + else + { + LOGIF(LE, (skygw_log_write( + LOGFILE_ERROR, + "Unknown option for CLI '%s'\n", + options[i]))); + } + } + } /* * We have completed the creation of the instance data, so now @@ -176,11 +199,15 @@ CLI_SESSION *client; spinlock_release(&inst->lock); session->state = SESSION_STATE_READY; + client->mode = inst->mode; dcb_printf(session->client, "Welcome the SkySQL MaxScale Debug Interface (%s).\n", version_str); - dcb_printf(session->client, "WARNING: This interface is meant for developer usage,\n"); - dcb_printf(session->client, "passing incorrect addresses to commands can endanger your MaxScale server.\n\n"); + if (client->mode == CLIM_DEVELOPER) + { + dcb_printf(session->client, "WARNING: This interface is meant for developer usage,\n"); + dcb_printf(session->client, "passing incorrect addresses to commands can endanger your MaxScale server.\n\n"); + } dcb_printf(session->client, "Type help for a list of available commands.\n\n"); return (void *)client; @@ -281,4 +308,4 @@ static uint8_t getCapabilities( void* router_session) { return 0; -} \ No newline at end of file +} diff --git a/server/modules/routing/debugcmd.c b/server/modules/routing/debugcmd.c index bf3aa888b..333ca8269 100644 --- a/server/modules/routing/debugcmd.c +++ b/server/modules/routing/debugcmd.c @@ -36,7 +36,10 @@ * Date Who Description * 20/06/13 Mark Riddoch Initial implementation * 17/07/13 Mark Riddoch Additional commands - * 09/08/2013 Massimiliano Pinto Addes enable/disable commands (now only for log) + * 09/08/2013 Massimiliano Pinto Added enable/disable commands (now only for log) + * 20/05/14 Mark Riddoch Added ability to give server and service names rather + * than simply addresses + * 23/05/14 Mark Riddoch Added support for developer and user modes * * @endverbatim */ @@ -69,6 +72,12 @@ #define ARG_TYPE_ADDRESS 1 #define ARG_TYPE_STRING 2 #define ARG_TYPE_SERVICE 3 +#define ARG_TYPE_SERVER 4 +#define ARG_TYPE_DBUSERS 5 +#define ARG_TYPE_SESSION 6 +#define ARG_TYPE_DCB 7 +#define ARG_TYPE_MONITOR 8 + /** * The subcommand structure * @@ -79,6 +88,7 @@ struct subcommand { int n_args; void (*fn)(); char *help; + char *devhelp; int arg_types[3]; }; @@ -87,33 +97,59 @@ static void telnetdShowUsers(DCB *); * The subcommands of the show command */ struct subcommand showoptions[] = { - { "dcbs", 0, dprintAllDCBs, "Show all descriptor control blocks (network connections)", + { "dcbs", 0, dprintAllDCBs, + "Show all descriptor control blocks (network connections)", + "Show all descriptor control blocks (network connections)", {0, 0, 0} }, - { "dcb", 1, dprintDCB, "Show a single descriptor control block e.g. show dcb 0x493340", - {ARG_TYPE_ADDRESS, 0, 0} }, - { "dbusers", 1, dcb_usersPrint, "Show statistics and user names for a service's user table.\n\t\tExample : show dbusers ", - {ARG_TYPE_ADDRESS, 0, 0} }, - { "epoll", 0, dprintPollStats, "Show the poll statistics", + { "dcb", 1, dprintDCB, + "Show a single descriptor control block e.g. show dcb 0x493340", + "Show a single descriptor control block e.g. show dcb 0x493340", + {ARG_TYPE_DCB, 0, 0} }, + { "dbusers", 1, dcb_usersPrint, + "Show statistics and user names for a service's user table.\n\t\tExample : show dbusers ", + "Show statistics and user names for a service's user table.\n\t\tExample : show dbusers |", + {ARG_TYPE_DBUSERS, 0, 0} }, + { "epoll", 0, dprintPollStats, + "Show the poll statistics", + "Show the poll statistics", {0, 0, 0} }, - { "modules", 0, dprintAllModules, "Show all currently loaded modules", + { "modules", 0, dprintAllModules, + "Show all currently loaded modules", + "Show all currently loaded modules", {0, 0, 0} }, - { "monitors", 0, monitorShowAll, "Show the monitors that are configured", + { "monitors", 0, monitorShowAll, + "Show the monitors that are configured", + "Show the monitors that are configured", {0, 0, 0} }, - { "server", 1, dprintServer, "Show details for a server, e.g. show server 0x485390", - {ARG_TYPE_ADDRESS, 0, 0} }, - { "servers", 0, dprintAllServers, "Show all configured servers", + { "server", 1, dprintServer, + "Show details for a named server, e.g. show server dbnode1", + "Show details for a server, e.g. show server 0x485390. The address may also be repalced with the server name from the configuration file", + {ARG_TYPE_SERVER, 0, 0} }, + { "servers", 0, dprintAllServers, + "Show all configured servers", + "Show all configured servers", {0, 0, 0} }, - { "services", 0, dprintAllServices, "Show all configured services in MaxScale", + { "services", 0, dprintAllServices, + "Show all configured services in MaxScale", + "Show all configured services in MaxScale", {0, 0, 0} }, - { "service", 1, dprintService, "Show single service in MaxScale", + { "service", 1, dprintService, + "Show a single service in MaxScale, may be passed a service name", + "Show a single service in MaxScale, may be passed a service name or address of a service object", {ARG_TYPE_SERVICE, 0, 0} }, - { "session", 1, dprintSession, "Show a single session in MaxScale, e.g. show session 0x284830", - {ARG_TYPE_ADDRESS, 0, 0} }, - { "sessions", 0, dprintAllSessions, "Show all active sessions in MaxScale", + { "session", 1, dprintSession, + "Show a single session in MaxScale, e.g. show session 0x284830", + "Show a single session in MaxScale, e.g. show session 0x284830", + {ARG_TYPE_SESSION, 0, 0} }, + { "sessions", 0, dprintAllSessions, + "Show all active sessions in MaxScale", + "Show all active sessions in MaxScale", {0, 0, 0} }, - { "users", 0, telnetdShowUsers, "Show statistics and user names for the debug interface", - {ARG_TYPE_ADDRESS, 0, 0} }, - { NULL, 0, NULL, NULL, + { "users", 0, telnetdShowUsers, + "Show statistics and user names for the debug interface", + "Show statistics and user names for the debug interface", + {0, 0, 0} }, + { NULL, 0, NULL, NULL, NULL, {0, 0, 0} } }; @@ -129,7 +165,7 @@ struct subcommand shutdownoptions[] = { 0, shutdown_server, "Shutdown MaxScale", - + "Shutdown MaxScale", {0, 0, 0} }, { @@ -137,13 +173,15 @@ struct subcommand shutdownoptions[] = { 1, shutdown_monitor, "Shutdown a monitor, e.g. shutdown monitor 0x48381e0", - {ARG_TYPE_ADDRESS, 0, 0} + "Shutdown a monitor, e.g. shutdown monitor 0x48381e0", + {ARG_TYPE_MONITOR, 0, 0} }, { "service", 1, shutdown_service, - "Shutdown a service, e.g. shutdown service 0x4838320", + "Shutdown a service, e.g. shutdown service \"Sales Database\"", + "Shutdown a service, e.g. shutdown service 0x4838320 or shutdown service \"Sales Database\"", {ARG_TYPE_SERVICE, 0, 0} }, { @@ -151,6 +189,7 @@ struct subcommand shutdownoptions[] = { 0, NULL, NULL, + NULL, {0, 0, 0} } }; @@ -162,11 +201,15 @@ static void restart_monitor(DCB *dcb, MONITOR *monitor); * The subcommands of the restart command */ struct subcommand restartoptions[] = { - { "monitor", 1, restart_monitor, "Restart a monitor, e.g. restart monitor 0x48181e0", - {ARG_TYPE_ADDRESS, 0, 0} }, - { "service", 1, restart_service, "Restart a service, e.g. restart service name", + { "monitor", 1, restart_monitor, + "Restart a monitor, e.g. restart monitor 0x48181e0", + "Restart a monitor, e.g. restart monitor 0x48181e0", + {ARG_TYPE_MONITOR, 0, 0} }, + { "service", 1, restart_service, + "Restart a service, e.g. restart service \"Test Service\"", + "Restart a service, e.g. restart service 0x4838320", {ARG_TYPE_SERVICE, 0, 0} }, - { NULL, 0, NULL, NULL, + { NULL, 0, NULL, NULL, NULL, {0, 0, 0} } }; @@ -175,9 +218,11 @@ static void set_server(DCB *dcb, SERVER *server, char *bit); * The subcommands of the set command */ struct subcommand setoptions[] = { - { "server", 2, set_server, "Set the status of a server. E.g. set server 0x4838320 master", - {ARG_TYPE_ADDRESS, ARG_TYPE_STRING, 0} }, - { NULL, 0, NULL, NULL, + { "server", 2, set_server, + "Set the status of a server. E.g. set server dbnode4 master", + "Set the status of a server. E.g. set server 0x4838320 master", + {ARG_TYPE_SERVER, ARG_TYPE_STRING, 0} }, + { NULL, 0, NULL, NULL, NULL, {0, 0, 0} } }; @@ -186,9 +231,11 @@ static void clear_server(DCB *dcb, SERVER *server, char *bit); * The subcommands of the clear command */ struct subcommand clearoptions[] = { - { "server", 2, clear_server, "Clear the status of a server. E.g. clear server 0x4838320 master", - {ARG_TYPE_ADDRESS, ARG_TYPE_STRING, 0} }, - { NULL, 0, NULL, NULL, + { "server", 2, clear_server, + "Clear the status of a server. E.g. clear server dbnode2 master", + "Clear the status of a server. E.g. clear server 0x4838320 master", + {ARG_TYPE_SERVER, ARG_TYPE_STRING, 0} }, + { NULL, 0, NULL, NULL, NULL, {0, 0, 0} } }; @@ -199,11 +246,15 @@ static void reload_config(DCB *dcb); * The subcommands of the reload command */ struct subcommand reloadoptions[] = { - { "config", 0, reload_config, "Reload the configuration data for MaxScale.", - {ARG_TYPE_ADDRESS, 0, 0} }, - { "dbusers", 1, reload_dbusers, "Reload the dbuser data for a service. E.g. reload dbusers 0x849420", - {ARG_TYPE_ADDRESS, 0, 0} }, - { NULL, 0, NULL, NULL, + { "config", 0, reload_config, + "Reload the configuration data for MaxScale.", + "Reload the configuration data for MaxScale.", + {0, 0, 0} }, + { "dbusers", 1, reload_dbusers, + "Reload the dbuser data for a service. E.g. reload dbusers \"splitter service\"", + "Reload the dbuser data for a service. E.g. reload dbusers 0x849420", + {ARG_TYPE_DBUSERS, 0, 0} }, + { NULL, 0, NULL, NULL, NULL, {0, 0, 0} } }; @@ -220,6 +271,8 @@ struct subcommand enableoptions[] = { enable_log_action, "Enable Log options for MaxScale, options trace | error | " "message E.g. enable log message.", + "Enable Log options for MaxScale, options trace | error | " + "message E.g. enable log message.", {ARG_TYPE_STRING, 0, 0} }, { @@ -227,6 +280,7 @@ struct subcommand enableoptions[] = { 0, NULL, NULL, + NULL, {0, 0, 0} } }; @@ -242,6 +296,8 @@ struct subcommand disableoptions[] = { disable_log_action, "Disable Log for MaxScale, Options: debug | trace | error | message " "E.g. disable log debug", + "Disable Log for MaxScale, Options: debug | trace | error | message " + "E.g. disable log debug", {ARG_TYPE_STRING, 0, 0} }, { @@ -249,6 +305,7 @@ struct subcommand disableoptions[] = { 0, NULL, NULL, + NULL, {0, 0, 0} } }; @@ -267,6 +324,7 @@ struct subcommand failoptions[] = { 0, fail_backendfd, "Fail backend socket for next operation.", + "Fail backend socket for next operation.", {ARG_TYPE_STRING, 0, 0} }, { @@ -274,6 +332,7 @@ struct subcommand failoptions[] = { 0, fail_clientfd, "Fail client socket for next operation.", + "Fail client socket for next operation.", {ARG_TYPE_STRING, 0, 0} }, { @@ -281,6 +340,7 @@ struct subcommand failoptions[] = { 2, fail_accept, "Fail to accept next client connection.", + "Fail to accept next client connection.", {ARG_TYPE_STRING, ARG_TYPE_STRING, 0} }, { @@ -288,6 +348,7 @@ struct subcommand failoptions[] = { 0, NULL, NULL, + NULL, {0, 0, 0} } }; @@ -298,9 +359,11 @@ static void telnetdAddUser(DCB *, char *, char *); * The subcommands of the add command */ struct subcommand addoptions[] = { - { "user", 2, telnetdAddUser, "Add a new user for the debug interface. E.g. add user john today", + { "user", 2, telnetdAddUser, + "Add a new user for the debug interface. E.g. add user john today", + "Add a new user for the debug interface. E.g. add user john today", {ARG_TYPE_STRING, ARG_TYPE_STRING, 0} }, - { NULL, 0, NULL, NULL, + { NULL, 0, NULL, NULL, NULL, {0, 0, 0} } }; @@ -315,10 +378,11 @@ struct subcommand removeoptions[] = { 2, telnetdRemoveUser, "Remove existing maxscale user. Example : remove user john johnpwd", + "Remove existing maxscale user. Example : remove user john johnpwd", {ARG_TYPE_STRING, ARG_TYPE_STRING, 0} }, { - NULL, 0, NULL, NULL, {0, 0, 0} + NULL, 0, NULL, NULL, NULL, {0, 0, 0} } }; @@ -351,14 +415,16 @@ static struct { * Convert a string argument to a numeric, observing prefixes * for number bases, e.g. 0x for hex, 0 for octal * + * @param mode The CLI mode * @param arg The string representation of the argument * @param arg_type The target type for the argument * @return The argument as a long integer */ static unsigned long -convert_arg(char *arg, int arg_type) +convert_arg(int mode, char *arg, int arg_type) { unsigned long rval; +SERVICE *service; switch (arg_type) { @@ -370,6 +436,38 @@ unsigned long rval; return (unsigned long)strtol(arg, NULL, 0); case ARG_TYPE_STRING: return (unsigned long)arg; + case ARG_TYPE_SERVICE: + if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0) + rval = (unsigned long)service_find(arg); + return rval; + case ARG_TYPE_SERVER: + if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0) + rval = (unsigned long)server_find_by_unique_name(arg); + return rval; + case ARG_TYPE_DBUSERS: + if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0) + { + service = service_find(arg); + if (service) + return (unsigned long)(service->users); + else + return 0; + } + return rval; + case ARG_TYPE_DCB: + rval = (unsigned long)strtol(arg, NULL, 0); + if (mode == CLIM_USER && dcb_isvalid((DCB *)rval) == 0) + rval = 0; + return rval; + case ARG_TYPE_SESSION: + rval = (unsigned long)strtol(arg, NULL, 0); + if (mode == CLIM_USER && session_isvalid((SESSION *)rval) == 0) + rval = 0; + return rval; + case ARG_TYPE_MONITOR: + if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0) + rval = (unsigned long)monitor_find(arg); + return rval; } return 0; } @@ -396,33 +494,94 @@ int argc, i, j, found = 0; char *args[MAXARGS]; char *saveptr, *delim = " \t\r\n"; unsigned long arg1, arg2, arg3; +int in_quotes = 0, escape_next = 0; +char *ptr, *lptr; - /* Tokenize the input string */ - args[0] = strtok_r(cli->cmdbuf, delim, &saveptr); + args[0] = cli->cmdbuf; + ptr = args[0]; + lptr = ptr; i = 0; - do { - i++; - args[i] = strtok_r(NULL, delim, &saveptr); - } while (args[i] != NULL && i < MAXARGS); + /* + * Break the command line into a number of words. Whitespace is used + * to delimit words and may be escaped by use of the \ character or + * the use of double quotes. + * The array args contains the broken down words, one per index. + */ + while (*ptr) + { + if (escape_next) + { + *lptr++ = *ptr++; + escape_next = 0; + } + else if (*ptr == '\\') + { + escape_next = 1; + ptr++; + } + else if (in_quotes == 0 && (*ptr == ' ' || *ptr == '\t' || *ptr == '\r' || *ptr == '\n')) + { + *lptr = 0; + if (args[i] == ptr) + args[i] = ptr + 1; + else + { + i++; + if (i >= MAXARGS) + break; + args[i] = ptr + 1; + } + ptr++; + lptr++; + } + else if (*ptr == '\"' && in_quotes == 0) + { + in_quotes = 1; + ptr++; + } + else if (*ptr == '\"' && in_quotes == 1) + { + in_quotes = 0; + ptr++; + } + else + { + *lptr++ = *ptr++; + } + } + *lptr = 0; + args[i+1] = NULL; - if (args[0] == NULL) + if (args[0] == NULL || *args[0] == 0) return 1; argc = i - 2; /* The number of extra arguments to commands */ if (!strcasecmp(args[0], "help")) { - if (args[1] == NULL) + if (args[1] == NULL || *args[1] == 0) { found = 1; dcb_printf(dcb, "Available commands:\n"); for (i = 0; cmds[i].cmd; i++) { - for (j = 0; cmds[i].options[j].arg1; j++) + if (cmds[i].options[1].arg1 == NULL) + dcb_printf(dcb, " %s %s\n", cmds[i].cmd, cmds[i].options[0].arg1); + else { - dcb_printf(dcb, " %s %s\n", cmds[i].cmd, cmds[i].options[j].arg1); + dcb_printf(dcb, " %s [", cmds[i].cmd); + for (j = 0; cmds[i].options[j].arg1; j++) + { + dcb_printf(dcb, "%s%s", cmds[i].options[j].arg1, + cmds[i].options[j+1].arg1 ? "|" : ""); + } + dcb_printf(dcb, "]\n"); } } + dcb_printf(dcb, "\nType help command to see details of each command.\n"); + dcb_printf(dcb, "Where commands require names as arguments and these names contain\n"); + dcb_printf(dcb, "whitespace either the \\ character may be used to escape the whitespace\n"); + dcb_printf(dcb, "or the name may be enclosed in double quotes \".\n\n"); } else { @@ -458,9 +617,9 @@ unsigned long arg1, arg2, arg3; { for (j = 0; cmds[i].options[j].arg1; j++) { - found = 1; /**< command and sub-command match */ if (strcasecmp(args[1], cmds[i].options[j].arg1) == 0) { + found = 1; /**< command and sub-command match */ if (argc != cmds[i].options[j].n_args) { dcb_printf(dcb, "Incorrect number of arguments: %s %s expects %d arguments\n", @@ -476,7 +635,7 @@ unsigned long arg1, arg2, arg3; cmds[i].options[j].fn(dcb); break; case 1: - arg1 = convert_arg(args[2],cmds[i].options[j].arg_types[0]); + arg1 = convert_arg(cli->mode, args[2],cmds[i].options[j].arg_types[0]); if (arg1) cmds[i].options[j].fn(dcb, arg1); else @@ -484,8 +643,8 @@ unsigned long arg1, arg2, arg3; args[2]); break; case 2: - arg1 = convert_arg(args[2],cmds[i].options[j].arg_types[0]); - arg2 = convert_arg(args[3],cmds[i].options[j].arg_types[1]); + arg1 = convert_arg(cli->mode, args[2],cmds[i].options[j].arg_types[0]); + arg2 = convert_arg(cli->mode, args[3],cmds[i].options[j].arg_types[1]); if (arg1 && arg2) cmds[i].options[j].fn(dcb, arg1, arg2); else if (arg1 == 0) @@ -496,9 +655,9 @@ unsigned long arg1, arg2, arg3; args[3]); break; case 3: - arg1 = convert_arg(args[2],cmds[i].options[j].arg_types[0]); - arg2 = convert_arg(args[3],cmds[i].options[j].arg_types[1]); - arg3 = convert_arg(args[4],cmds[i].options[j].arg_types[2]); + arg1 = convert_arg(cli->mode, args[2],cmds[i].options[j].arg_types[0]); + arg2 = convert_arg(cli->mode, args[3],cmds[i].options[j].arg_types[1]); + arg3 = convert_arg(cli->mode, args[4],cmds[i].options[j].arg_types[2]); if (arg1 && arg2 && arg3) cmds[i].options[j].fn(dcb, arg1, arg2, arg3); else if (arg1 == 0) diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index 614c5c3e8..e4977f55e 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -725,6 +725,7 @@ static int routeQuery( } else if (QUERY_IS_TYPE(qtype, QUERY_TYPE_READ) && !router_cli_ses->rses_transaction_active) + { LOGIF(LT, (skygw_log_write( LOGFILE_TRACE, diff --git a/server/modules/routing/readwritesplit/test/rwsplit.sh b/server/modules/routing/readwritesplit/test/rwsplit.sh index 6c5268d46..56d6f1f90 100755 --- a/server/modules/routing/readwritesplit/test/rwsplit.sh +++ b/server/modules/routing/readwritesplit/test/rwsplit.sh @@ -191,3 +191,4 @@ else echo "$TINPUT PASSED">>$TLOG ; fi +