diff --git a/doc/internal/global_variables.txt b/doc/internal/global_variables.txt index f7ab002bdc..d3406864dd 100644 --- a/doc/internal/global_variables.txt +++ b/doc/internal/global_variables.txt @@ -5,6 +5,7 @@ either very simple, or managed by a simple API. Trying to make a standard API, the proposed functions are: virtual char **get_variables_list() {return NULL;} + virtual bool has_variable(char *name) {return false;} virtual char *get_variable(char *name) {return NULL;}; virtual bool set_variable(char *name, char *value) {return false;}; virtual void commit() {}; @@ -14,6 +15,8 @@ Trying to make a standard API, the proposed functions are: In order: - get_variables_list() returns an array of pointer to variables name, where the last element is NULL; +- has_variable() returns true if the given name is the name of one of the module + variables; - get_variable() returns the value of a specific variable. If the variable was not previously set, the module should return the default value. If the variable doesn't exist, NULL should be returned; diff --git a/include/MySQL_Thread.h b/include/MySQL_Thread.h index 99f081633a..8b6e767ff8 100644 --- a/include/MySQL_Thread.h +++ b/include/MySQL_Thread.h @@ -311,6 +311,7 @@ class MySQL_Threads_Handler char *get_variable(char *name); bool set_variable(char *name, char *value); char **get_variables_list(); + bool has_variable(char * name); MySQL_Threads_Handler(); ~MySQL_Threads_Handler(); diff --git a/include/gen_utils.h b/include/gen_utils.h index 260836db70..5a9cdb0cff 100644 --- a/include/gen_utils.h +++ b/include/gen_utils.h @@ -153,3 +153,4 @@ bool Proxy_file_exists(const char *); bool Proxy_file_regular(const char *); int remove_spaces(const char *); +char *trim_spaces_in_place(char *str); \ No newline at end of file diff --git a/include/proxysql_admin.h b/include/proxysql_admin.h index 1039b1219e..58f900417b 100644 --- a/include/proxysql_admin.h +++ b/include/proxysql_admin.h @@ -92,6 +92,7 @@ class ProxySQL_Admin { bool init(); bool get_read_only() { return variables.admin_read_only; } bool set_read_only(bool ro) { variables.admin_read_only=ro; return variables.admin_read_only; } + bool has_variable(char * name); void init_users(); void init_mysql_servers(); void init_mysql_query_rules(); diff --git a/lib/MySQL_Thread.cpp b/lib/MySQL_Thread.cpp index b6874ebf00..a2f5788f4b 100644 --- a/lib/MySQL_Thread.cpp +++ b/lib/MySQL_Thread.cpp @@ -292,7 +292,7 @@ int MySQL_Threads_Handler::listener_add(const char *iface) { while(!__sync_bool_compare_and_swap(&thr->mypolls.pending_listener_add,0,rc)) { usleep(100); // pause a bit } -/* +/* while(!__sync_bool_compare_and_swap(&thr->mypolls.pending_listener_change,0,1)) { cpu_relax_pa(); } while(__sync_fetch_and_add(&thr->mypolls.pending_listener_change,0)==1) { cpu_relax_pa(); } // spin_wrlock(&thr->thread_mutex); @@ -323,7 +323,7 @@ int MySQL_Threads_Handler::listener_del(const char *iface) { shutdown(fd,SHUT_RDWR); close(fd); } - + return 0; } @@ -618,7 +618,7 @@ char * MySQL_Threads_Handler::get_variable(char *name) { // this is the public f return strdup((variables.default_reconnect ? "true" : "false")); } return NULL; -} +} @@ -630,7 +630,7 @@ bool MySQL_Threads_Handler::set_variable(char *name, char *value) { // this is t // OUT: // false: unable to change the variable value, either because doesn't exist, or because out of range, or read only // true: variable value changed - // + // if (!value) return false; size_t vallen=strlen(value); @@ -1175,6 +1175,18 @@ char ** MySQL_Threads_Handler::get_variables_list() { return ret; } +// Returns true if the given name is the name of an existing mysql variable +bool MySQL_Threads_Handler::has_variable(char *name) { + size_t no_vars = sizeof(mysql_thread_variables_names) / sizeof(char *); + for (unsigned int i = 0; i < no_vars, mysql_thread_variables_names[i] != NULL; ++i) { + size_t var_len = strlen(mysql_thread_variables_names[i]); + if (strlen(name) == var_len && !strncmp(name, mysql_thread_variables_names[i], var_len)) { + return true; + } + } + return false; +} + void MySQL_Threads_Handler::print_version() { fprintf(stderr,"Standard MySQL Threads Handler rev. %s -- %s -- %s\n", MYSQL_THREAD_VERSION, __FILE__, __TIMESTAMP__); } @@ -1345,8 +1357,8 @@ void MySQL_Thread::poll_listener_add(int sock) { listener_DS->fd=sock; proxy_debug(PROXY_DEBUG_NET,1,"Created listener %p for socket %d\n", listener_DS, sock); - //mypoll_add(&mypolls, POLLIN, sock, listener_DS); - mypolls.add(POLLIN, sock, listener_DS, monotonic_time()); + //mypoll_add(&mypolls, POLLIN, sock, listener_DS); + mypolls.add(POLLIN, sock, listener_DS, monotonic_time()); } void MySQL_Thread::poll_listener_del(int sock) { @@ -1489,7 +1501,7 @@ void MySQL_Thread::run() { proxy_debug(PROXY_DEBUG_NET,1,"Poll for DataStream=%p will be called with FD=%d and events=%d\n", mypolls.myds[n], mypolls.fds[n].fd, mypolls.fds[n].events); } - + spin_wrunlock(&thread_mutex); while ((n=__sync_add_and_fetch(&mypolls.pending_listener_add,0))) { // spin here @@ -1498,7 +1510,7 @@ void MySQL_Thread::run() { // if (n==1) { // __sync_add_and_fetch(&mypolls.pending_listener_change,1); // } - } + } if (mysql_thread___wait_timeout==0) { // we should be going into PAUSE mode @@ -1528,7 +1540,7 @@ void MySQL_Thread::run() { spin_wrlock(&thread_mutex); mypolls.poll_timeout=0; // always reset this to 0 . If a session needs a specific timeout, it will set this one - + curtime=monotonic_time(); if (curtime > last_maintenance_time + 200000) { // hardcoded value for now last_maintenance_time=curtime; @@ -1646,7 +1658,7 @@ void MySQL_Thread::process_data_on_data_stream(MySQL_Data_Stream *myds, unsigned myds->myconn->handler(mypolls.fds[n].revents); } } - if ( (mypolls.fds[n].events & POLLOUT) + if ( (mypolls.fds[n].events & POLLOUT) && ( (mypolls.fds[n].revents & POLLERR) || (mypolls.fds[n].revents & POLLHUP) ) ) { @@ -1654,7 +1666,7 @@ void MySQL_Thread::process_data_on_data_stream(MySQL_Data_Stream *myds, unsigned } myds->check_data_flow(); - + if (myds->active==FALSE) { if (myds->sess->client_myds==myds) { diff --git a/lib/ProxySQL_Admin.cpp b/lib/ProxySQL_Admin.cpp index 2d28f7792b..1a39b80d21 100644 --- a/lib/ProxySQL_Admin.cpp +++ b/lib/ProxySQL_Admin.cpp @@ -212,7 +212,7 @@ class admin_main_loop_listeners { } return ifaces; } - + public: int nfds; @@ -445,19 +445,37 @@ bool admin_handler_command_set(char *query_no_space, unsigned int query_no_space // Get a pointer to the beginnig of var=value entry char *set_entry = query_no_space + strlen("SET "); - char *variable; - char *value; - c_split_2(set_entry, "=", &variable, &value); + char *var_name; + char *var_value; + c_split_2(set_entry, "=", &var_name, &var_value); + + proxy_info("Set variable '%s' to value '%s'\n", var_name, var_value); + + char *t_var_name = trim_spaces_in_place(var_name); - proxy_info("Set variable '%s' to value '%s'\n", variable, value); - // TODO(iprunache) - validate variables; - // TODO(iprunache) - parse variables to extract table + // Check if the command tries to set a non-existing variable. + if (strlen(t_var_name) > 6 && !strncmp(t_var_name, "mysql-", 6)) { + // Strip the var type prefix since it's not stored internally. + if (!GloMTH->has_variable(t_var_name + 6)) { + SPA->send_MySQL_ERR(&sess->client_myds->myprot, (char *) "Unknown mysql variable."); + return false; + } + } else if (strlen(t_var_name) > 6 && !strncmp(t_var_name, "admin-", 6)) { + // Strip the var type prefix since it's not stored internally. + if (!SPA->has_variable(t_var_name + 6)) { + SPA->send_MySQL_ERR(&sess->client_myds->myprot, (char *) "Unknown admin variable."); + return false; + } + } else { + SPA->send_MySQL_ERR(&sess->client_myds->myprot, (char *) "Unknown variable."); + return false; + } // TODO(iprunache) - build UPDATE command // TODO(iprunache) - run UPDATE command // TODO(iprunache) - cleanup debug prints - free(variable); - free(value); + free(var_name); + free(var_value); SPA->send_MySQL_OK(&sess->client_myds->myprot, NULL); return false; } @@ -1267,7 +1285,7 @@ void admin_session_handler(MySQL_Session *sess, ProxySQL_Admin *pa, PtrSize_t *p memcpy(query,(char *)pkt->ptr+sizeof(mysql_hdr)+1,query_length-1); query[query_length-1]=0; - char *query_no_space=(char *)l_alloc(query_length); + char *query_no_space=(char *)l_alloc(query_length); memcpy(query_no_space,query,query_length); unsigned int query_no_space_length=remove_spaces(query_no_space); @@ -1305,16 +1323,16 @@ void admin_session_handler(MySQL_Session *sess, ProxySQL_Admin *pa, PtrSize_t *p } if (sess->stats==false) { - if ((query_no_space_length>8) && (!strncasecmp("PROXYSQL ", query_no_space, 8))) { + if ((query_no_space_length>8) && (!strncasecmp("PROXYSQL ", query_no_space, 8))) { proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received PROXYSQL command\n"); pthread_mutex_lock(&admin_mutex); run_query=admin_handler_command_proxysql(query_no_space, query_no_space_length, sess, pa); pthread_mutex_unlock(&admin_mutex); goto __run_query; } - if ((query_no_space_length>5) && ( (!strncasecmp("SAVE ", query_no_space, 5)) || (!strncasecmp("LOAD ", query_no_space, 5))) ) { + if ((query_no_space_length>5) && ( (!strncasecmp("SAVE ", query_no_space, 5)) || (!strncasecmp("LOAD ", query_no_space, 5))) ) { proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received LOAD or SAVE command\n"); - run_query=admin_handler_command_load_or_save(query_no_space, query_no_space_length, sess, pa, &query, &query_length); + run_query=admin_handler_command_load_or_save(query_no_space, query_no_space_length, sess, pa, &query, &query_length); goto __run_query; } if ((query_no_space_length>16) && ( (!strncasecmp("KILL CONNECTION ", query_no_space, 16)) || (!strncasecmp("KILL CONNECTION ", query_no_space, 16))) ) { @@ -1826,7 +1844,7 @@ void *child_mysql(void *arg) { MySQL_Data_Stream *myds=sess->client_myds; fds[0].fd=client; - fds[0].revents=0; + fds[0].revents=0; fds[0].events=POLLIN|POLLOUT; //sess->myprot_client.generate_pkt_initial_handshake(sess->client_myds,true,NULL,NULL); @@ -1834,14 +1852,14 @@ void *child_mysql(void *arg) { unsigned long oldtime=monotonic_time(); unsigned long curtime=monotonic_time(); - + while (__sync_fetch_and_add(&glovars.shutdown,0)==0) { if (myds->available_data_out()) { - fds[0].events=POLLIN|POLLOUT; + fds[0].events=POLLIN|POLLOUT; } else { - fds[0].events=POLLIN; + fds[0].events=POLLIN; } - fds[0].revents=0; + fds[0].revents=0; //rc=poll(fds,nfds,2000); rc=poll(fds,nfds,__sync_fetch_and_add(&__admin_refresh_interval,0)); { @@ -1877,13 +1895,13 @@ void *child_mysql(void *arg) { //delete sess; if (mysql_thread___default_schema) { free(mysql_thread___default_schema); mysql_thread___default_schema=NULL; } if (mysql_thread___server_version) { free(mysql_thread___server_version); mysql_thread___server_version=NULL; } - delete mysql_thr; -// l_mem_destroy(__thr_sfp); + delete mysql_thr; +// l_mem_destroy(__thr_sfp); return NULL; } void* child_telnet(void* arg) -{ +{ int bytes_read; //int i; // struct timeval tv; @@ -1896,7 +1914,7 @@ void* child_telnet(void* arg) memset(line,0,LINESIZE+1); while ((strncmp(line, "quit", 4) != 0) && glovars.shutdown==0) { bytes_read = recv(client, line, LINESIZE, 0); - if (bytes_read==-1) { + if (bytes_read==-1) { break; } char *eow = strchr(line, '\n'); @@ -1914,7 +1932,7 @@ void* child_telnet(void* arg) } void* child_telnet_also(void* arg) -{ +{ int bytes_read; //int i; // struct timeval tv; @@ -1927,7 +1945,7 @@ void* child_telnet_also(void* arg) memset(line,0,LINESIZE+1); while ((strncmp(line, "quit", 4) != 0) && glovars.shutdown==0) { bytes_read = recv(client, line, LINESIZE, 0); - if (bytes_read==-1) { + if (bytes_read==-1) { break; } char *eow = strchr(line, '\n'); @@ -1961,7 +1979,7 @@ static void * admin_main_loop(void *arg) volatile int *shutdown=((struct _main_args *)arg)->shutdown; char *socket_names[MAX_ADMIN_LISTENERS]; for (i=0;i0) { fds[nfds].fd=s; fds[nfds].events=POLLIN; fds[nfds].revents=0; callback_func[nfds]=2; socket_names[nfds]=strdup(sn); nfds++; } // } - S_amll.wrunlock(); + S_amll.wrunlock(); } - + } //if (__sync_add_and_fetch(shutdown,0)==0) __sync_add_and_fetch(shutdown,1); for (i=0; iexecute("ATTACH DATABASE 'file:mem_mydb?mode=memory&cache=shared' AS myhgm"); #endif /* DEBUG */ @@ -2211,16 +2229,16 @@ bool ProxySQL_Admin::init() { if (GloVars.__cmd_proxysql_reload || GloVars.__cmd_proxysql_initial) { if (GloVars.configfile_open) { - if (GloVars.confFile->cfg) { - Read_MySQL_Servers_from_configfile(); + if (GloVars.confFile->cfg) { + Read_MySQL_Servers_from_configfile(); Read_Global_Variables_from_configfile("admin"); Read_Global_Variables_from_configfile("mysql"); Read_MySQL_Users_from_configfile(); Read_MySQL_Query_Rules_from_configfile(); __insert_or_replace_disktable_select_maintable(); } else { - if (GloVars.confFile->OpenFile(GloVars.config_file)==true) { - Read_MySQL_Servers_from_configfile(); + if (GloVars.confFile->OpenFile(GloVars.config_file)==true) { + Read_MySQL_Servers_from_configfile(); Read_MySQL_Users_from_configfile(); Read_MySQL_Query_Rules_from_configfile(); Read_Global_Variables_from_configfile("admin"); @@ -2237,7 +2255,7 @@ bool ProxySQL_Admin::init() { S_amll.update_ifaces(variables.telnet_admin_ifaces, &S_amll.ifaces_telnet_admin); S_amll.update_ifaces(variables.telnet_stats_ifaces, &S_amll.ifaces_telnet_stats); - + // pthread_t admin_thr; struct _main_args *arg=(struct _main_args *)malloc(sizeof(struct _main_args)); @@ -2488,7 +2506,7 @@ void ProxySQL_Admin::flush_mysql_variables___runtime_to_database(SQLite3DB *db, } int l=strlen(a)+200; GloMTH->wrlock(); - char **varnames=GloMTH->get_variables_list(); + char **varnames=GloMTH->get_variables_list(); char *query=(char *)malloc(l); for (int i=0; varnames[i]; i++) { char *val=GloMTH->get_variable(varnames[i]); @@ -2514,6 +2532,19 @@ char **ProxySQL_Admin::get_variables_list() { return ret; } + +// Returns true if the given name is the name of an existing admin variable +bool ProxySQL_Admin::has_variable(char *name) { + size_t no_vars = sizeof(admin_variables_names) / sizeof(char *); + for (unsigned int i = 0; i < no_vars, admin_variables_names[i] != NULL; ++i) { + size_t var_len = strlen(admin_variables_names[i]); + if (strlen(name) == var_len && !strncmp(name, admin_variables_names[i], var_len)) { + return true; + } + } + return false; +} + char * ProxySQL_Admin::get_variable(char *name) { #define INTBUFSIZE 4096 char intbuf[INTBUFSIZE]; @@ -3007,7 +3038,7 @@ void ProxySQL_Admin::flush_admin_variables___runtime_to_database(SQLite3DB *db, } int l=strlen(a)+200; - char **varnames=get_variables_list(); + char **varnames=get_variables_list(); char *query=(char *)malloc(l); for (int i=0; varnames[i]; i++) { char *val=get_variable(varnames[i]); diff --git a/lib/gen_utils.cpp b/lib/gen_utils.cpp index 407787f47c..770ac5a9bf 100644 --- a/lib/gen_utils.cpp +++ b/lib/gen_utils.cpp @@ -30,6 +30,36 @@ int remove_spaces(const char *s) { return strlen(s); } +// This function returns a pointer to a substring of the original string. It also +// modifies the original string by setting a null terminator to mark the end +// of the substring. +// +// If the given string was allocated dynamically, the caller must not overwrite +// that pointer with the returned value, since the original pointer must be +// deallocated using the same allocator with which it was allocated. The return +// value must NOT be deallocated using free() etc. +// +// Source: http://stackoverflow.com/a/122721 +char *trim_spaces_in_place(char *str) +{ + char *end; + + // Trim leading space + while(isspace(*str)) str++; + + if(*str == 0) // All spaces? + return str; + + // Trim trailing space + end = str + strlen(str) - 1; + while(end > str && isspace(*end)) end--; + + // Write new null terminator + *(end+1) = 0; + + return str; +} + #define MIN_ARRAY_LEN 8 #define MIN_ARRAY_DELETE_RATIO 8