moving files to /server to make merge possible
This commit is contained in:
60
server/modules/include/debugcli.h
Normal file
60
server/modules/include/debugcli.h
Normal file
@ -0,0 +1,60 @@
|
||||
#ifndef _DEBUGCLI_H
|
||||
#define _DEBUGCLI_H
|
||||
/*
|
||||
* This file is distributed as part of the SkySQL Gateway. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright SkySQL Ab 2013
|
||||
*/
|
||||
#include <service.h>
|
||||
#include <session.h>
|
||||
#include <spinlock.h>
|
||||
|
||||
/**
|
||||
* @file debugcli.h The debug interface to the gateway
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 18/06/13 Mark Riddoch Initial implementation
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
struct cli_session;
|
||||
|
||||
/**
|
||||
* The CLI_INSTANCE structure. There is one instane of the CLI "router" for
|
||||
* each service that uses the CLI.
|
||||
*/
|
||||
typedef struct cli_instance {
|
||||
SPINLOCK lock; /**< The instance spinlock */
|
||||
SERVICE *service; /**< The debug cli service */
|
||||
struct cli_session
|
||||
*sessions; /**< Linked list of sessions within this instance */
|
||||
struct cli_instance
|
||||
*next; /**< The next pointer for the list of instances */
|
||||
} CLI_INSTANCE;
|
||||
|
||||
/**
|
||||
* The CLI_SESSION structure. As CLI_SESSION is created for each user that logs into
|
||||
* the DEBUG CLI.
|
||||
*/
|
||||
typedef struct cli_session {
|
||||
char cmdbuf[80]; /**< The command buffer used to build up user commands */
|
||||
SESSION *session; /**< The gateway session */
|
||||
struct cli_session
|
||||
*next; /**< The next pointer for the list of sessions */
|
||||
} CLI_SESSION;
|
||||
#endif
|
||||
65
server/modules/include/httpd.h
Normal file
65
server/modules/include/httpd.h
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* This file is distributed as part of the SkySQL Gateway. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright SkySQL Ab 2013
|
||||
*/
|
||||
|
||||
/*
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 08-07-2013 Massimiliano Pinto Added HTTPD protocol header file
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dcb.h>
|
||||
#include <buffer.h>
|
||||
#include <service.h>
|
||||
#include <session.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <router.h>
|
||||
#include <poll.h>
|
||||
#include <atomic.h>
|
||||
#include <gw.h>
|
||||
|
||||
#define HTTPD_SMALL_BUFFER 1024
|
||||
#define HTTPD_METHOD_MAXLEN 128
|
||||
#define HTTPD_USER_MAXLEN 128
|
||||
#define HTTPD_HOSTNAME_MAXLEN 512
|
||||
#define HTTPD_USERAGENT_MAXLEN 1024
|
||||
#define HTTPD_FIELD_MAXLEN 8192
|
||||
#define HTTPD_REQUESTLINE_MAXLEN 8192
|
||||
|
||||
/**
|
||||
* HTTPD session specific data
|
||||
*
|
||||
*/
|
||||
typedef struct httpd_session {
|
||||
char user[HTTPD_USER_MAXLEN]; /**< username for authentication*/
|
||||
char *cookies; /**< all input cookies */
|
||||
char hostname[HTTPD_HOSTNAME_MAXLEN]; /**< The hostname */
|
||||
char useragent[HTTPD_USERAGENT_MAXLEN]; /**< The useragent */
|
||||
char method[HTTPD_METHOD_MAXLEN]; /**< The HTTPD Method */
|
||||
char *url; /**< the URL in the request */
|
||||
char *path_info; /**< the Pathinfo, starts with /, is the extra path segments after the document name */
|
||||
char *query_string; /**< the Query string, starts with ?, after path_info and document name */
|
||||
int headers_received; /**< All the headers has been received, if 1 */
|
||||
} HTTPD_session;
|
||||
250
server/modules/include/mysql_client_server_protocol.h
Normal file
250
server/modules/include/mysql_client_server_protocol.h
Normal file
@ -0,0 +1,250 @@
|
||||
#ifndef _MYSQL_PROTOCOL_H
|
||||
#define _MYSQL_PROTOCOL_H
|
||||
/*
|
||||
* This file is distributed as part of the SkySQL Gateway. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright SkySQL Ab 2013
|
||||
*/
|
||||
|
||||
/*
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 01-06-2013 Mark Riddoch Initial implementation
|
||||
* 14-06-2013 Massimiliano Pinto Added specific data
|
||||
* for MySQL session
|
||||
* 04-07-2013 Massimiliano Pinto Added new MySQL protocol status for asynchronous connection
|
||||
* Added authentication reply status
|
||||
* 12-07-2013 Massimiliano Pinto Added routines for change_user
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdbool.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <service.h>
|
||||
#include <router.h>
|
||||
#include <poll.h>
|
||||
#include <users.h>
|
||||
|
||||
#ifndef MYSQL_SCRAMBLE_LEN
|
||||
#define MYSQL_SCRAMBLE_LEN GW_MYSQL_SCRAMBLE_SIZE
|
||||
#endif
|
||||
|
||||
#define MYSQL_USER_MAXLEN 128
|
||||
#define MYSQL_DATABASE_MAXLEN 128
|
||||
|
||||
#define GW_VERSION "0.1.0"
|
||||
#define GW_MYSQL_VERSION "5.5.22-SKYSQL-" GW_VERSION
|
||||
#define GW_MYSQL_LOOP_TIMEOUT 300000000
|
||||
#define GW_MYSQL_READ 0
|
||||
#define GW_MYSQL_WRITE 1
|
||||
|
||||
#define GW_MYSQL_PROTOCOL_VERSION 10 // version is 10
|
||||
#define GW_MYSQL_HANDSHAKE_FILLER 0x00
|
||||
#define GW_MYSQL_SERVER_CAPABILITIES_BYTE1 0xff
|
||||
#define GW_MYSQL_SERVER_CAPABILITIES_BYTE2 0xf7
|
||||
#define GW_MYSQL_SERVER_LANGUAGE 0x08
|
||||
#define GW_MYSQL_MAX_PACKET_LEN 0xffffffL;
|
||||
#define GW_MYSQL_SCRAMBLE_SIZE 20
|
||||
|
||||
#define GW_NOINTR_CALL(A) do { errno = 0; A; } while (errno == EINTR)
|
||||
// network buffer is 32K
|
||||
#define MAX_BUFFER_SIZE 32768
|
||||
// socket send buffer for backend
|
||||
#define GW_BACKEND_SO_SNDBUF 1024
|
||||
#define SMALL_CHUNK 1024
|
||||
#define MAX_CHUNK SMALL_CHUNK * 8 * 4
|
||||
#define ToHex(Y) (Y>='0'&&Y<='9'?Y-'0':Y-'A'+10)
|
||||
|
||||
struct dcb;
|
||||
|
||||
/*
|
||||
* MySQL Protocol specific state data
|
||||
*/
|
||||
typedef struct {
|
||||
int fd; /* The socket descriptor */
|
||||
struct dcb *descriptor; /* The DCB of the socket we are running on */
|
||||
int state; /* Current descriptor state */
|
||||
uint8_t scramble[MYSQL_SCRAMBLE_LEN]; /* server scramble, created or received */
|
||||
uint32_t server_capabilities; /* server capabilities, created or received */
|
||||
uint32_t client_capabilities; /* client capabilities, created or received */
|
||||
unsigned long tid; /* MySQL Thread ID, in handshake */
|
||||
} MySQLProtocol;
|
||||
|
||||
/*
|
||||
* MySQL session specific data
|
||||
*
|
||||
*/
|
||||
typedef struct mysql_session {
|
||||
uint8_t client_sha1[MYSQL_SCRAMBLE_LEN]; /* SHA1(passowrd) */
|
||||
char user[MYSQL_USER_MAXLEN]; /* username */
|
||||
char db[MYSQL_DATABASE_MAXLEN]; /* database */
|
||||
} MYSQL_session;
|
||||
|
||||
/* MySQL Protocol States */
|
||||
#define MYSQL_ALLOC 0 /* Allocate data */
|
||||
#define MYSQL_PENDING_CONNECT 1 /* Backend socket pending connect */
|
||||
#define MYSQL_CONNECTED 2 /* Backend socket Connected */
|
||||
#define MYSQL_AUTH_SENT 3 /* Authentication handshake has been sent */
|
||||
#define MYSQL_AUTH_RECV 4 /* Received user, password, db and capabilities */
|
||||
#define MYSQL_AUTH_FAILED 5 /* Auth failed, return error packet */
|
||||
#define MYSQL_IDLE 6 /* Auth done. Protocol is idle, waiting for statements */
|
||||
#define MYSQL_ROUTING 7 /* The received command has been routed to backend(s) */
|
||||
#define MYSQL_WAITING_RESULT 8 /* Waiting for result set */
|
||||
#define MYSQL_SESSION_CHANGE 9 /* Pending session change */
|
||||
|
||||
/* MySQL states for authentication reply */
|
||||
#define MYSQL_FAILED_AUTHENTICATION 1
|
||||
#define MYSQL_SUCCESFUL_AUTHENTICATION 0
|
||||
|
||||
/* Protocol packing macros. */
|
||||
#define gw_mysql_set_byte2(__buffer, __int) do { \
|
||||
(__buffer)[0]= (uint8_t)((__int) & 0xFF); \
|
||||
(__buffer)[1]= (uint8_t)(((__int) >> 8) & 0xFF); } while (0)
|
||||
#define gw_mysql_set_byte3(__buffer, __int) do { \
|
||||
(__buffer)[0]= (uint8_t)((__int) & 0xFF); \
|
||||
(__buffer)[1]= (uint8_t)(((__int) >> 8) & 0xFF); \
|
||||
(__buffer)[2]= (uint8_t)(((__int) >> 16) & 0xFF); } while (0)
|
||||
#define gw_mysql_set_byte4(__buffer, __int) do { \
|
||||
(__buffer)[0]= (uint8_t)((__int) & 0xFF); \
|
||||
(__buffer)[1]= (uint8_t)(((__int) >> 8) & 0xFF); \
|
||||
(__buffer)[2]= (uint8_t)(((__int) >> 16) & 0xFF); \
|
||||
(__buffer)[3]= (uint8_t)(((__int) >> 24) & 0xFF); } while (0)
|
||||
|
||||
/* Protocol unpacking macros. */
|
||||
#define gw_mysql_get_byte2(__buffer) \
|
||||
(uint16_t)((__buffer)[0] | \
|
||||
((__buffer)[1] << 8))
|
||||
#define gw_mysql_get_byte3(__buffer) \
|
||||
(uint32_t)((__buffer)[0] | \
|
||||
((__buffer)[1] << 8) | \
|
||||
((__buffer)[2] << 16))
|
||||
#define gw_mysql_get_byte4(__buffer) \
|
||||
(uint32_t)((__buffer)[0] | \
|
||||
((__buffer)[1] << 8) | \
|
||||
((__buffer)[2] << 16) | \
|
||||
((__buffer)[3] << 24))
|
||||
#define gw_mysql_get_byte8(__buffer) \
|
||||
((uint64_t)(__buffer)[0] | \
|
||||
((uint64_t)(__buffer)[1] << 8) | \
|
||||
((uint64_t)(__buffer)[2] << 16) | \
|
||||
((uint64_t)(__buffer)[3] << 24) | \
|
||||
((uint64_t)(__buffer)[4] << 32) | \
|
||||
((uint64_t)(__buffer)[5] << 40) | \
|
||||
((uint64_t)(__buffer)[6] << 48) | \
|
||||
((uint64_t)(__buffer)[7] << 56))
|
||||
|
||||
/* MySQL protocol constants */
|
||||
typedef enum
|
||||
{
|
||||
GW_MYSQL_CAPABILITIES_NONE= 0,
|
||||
GW_MYSQL_CAPABILITIES_LONG_PASSWORD= (1 << 0),
|
||||
GW_MYSQL_CAPABILITIES_FOUND_ROWS= (1 << 1),
|
||||
GW_MYSQL_CAPABILITIES_LONG_FLAG= (1 << 2),
|
||||
GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB= (1 << 3),
|
||||
GW_MYSQL_CAPABILITIES_NO_SCHEMA= (1 << 4),
|
||||
GW_MYSQL_CAPABILITIES_COMPRESS= (1 << 5),
|
||||
GW_MYSQL_CAPABILITIES_ODBC= (1 << 6),
|
||||
GW_MYSQL_CAPABILITIES_LOCAL_FILES= (1 << 7),
|
||||
GW_MYSQL_CAPABILITIES_IGNORE_SPACE= (1 << 8),
|
||||
GW_MYSQL_CAPABILITIES_PROTOCOL_41= (1 << 9),
|
||||
GW_MYSQL_CAPABILITIES_INTERACTIVE= (1 << 10),
|
||||
GW_MYSQL_CAPABILITIES_SSL= (1 << 11),
|
||||
GW_MYSQL_CAPABILITIES_IGNORE_SIGPIPE= (1 << 12),
|
||||
GW_MYSQL_CAPABILITIES_TRANSACTIONS= (1 << 13),
|
||||
GW_MYSQL_CAPABILITIES_RESERVED= (1 << 14),
|
||||
GW_MYSQL_CAPABILITIES_SECURE_CONNECTION= (1 << 15),
|
||||
GW_MYSQL_CAPABILITIES_MULTI_STATEMENTS= (1 << 16),
|
||||
GW_MYSQL_CAPABILITIES_MULTI_RESULTS= (1 << 17),
|
||||
GW_MYSQL_CAPABILITIES_PS_MULTI_RESULTS= (1 << 18),
|
||||
GW_MYSQL_CAPABILITIES_PLUGIN_AUTH= (1 << 19),
|
||||
GW_MYSQL_CAPABILITIES_SSL_VERIFY_SERVER_CERT= (1 << 30),
|
||||
GW_MYSQL_CAPABILITIES_REMEMBER_OPTIONS= (1 << 31),
|
||||
GW_MYSQL_CAPABILITIES_CLIENT= (GW_MYSQL_CAPABILITIES_LONG_PASSWORD |
|
||||
GW_MYSQL_CAPABILITIES_FOUND_ROWS |
|
||||
GW_MYSQL_CAPABILITIES_LONG_FLAG |
|
||||
GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB |
|
||||
GW_MYSQL_CAPABILITIES_LOCAL_FILES |
|
||||
GW_MYSQL_CAPABILITIES_PLUGIN_AUTH |
|
||||
GW_MYSQL_CAPABILITIES_TRANSACTIONS |
|
||||
GW_MYSQL_CAPABILITIES_PROTOCOL_41 |
|
||||
GW_MYSQL_CAPABILITIES_MULTI_STATEMENTS |
|
||||
GW_MYSQL_CAPABILITIES_MULTI_RESULTS |
|
||||
GW_MYSQL_CAPABILITIES_PS_MULTI_RESULTS |
|
||||
GW_MYSQL_CAPABILITIES_SECURE_CONNECTION),
|
||||
GW_MYSQL_CAPABILITIES_CLIENT_COMPRESS= (GW_MYSQL_CAPABILITIES_LONG_PASSWORD |
|
||||
GW_MYSQL_CAPABILITIES_FOUND_ROWS |
|
||||
GW_MYSQL_CAPABILITIES_LONG_FLAG |
|
||||
GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB |
|
||||
GW_MYSQL_CAPABILITIES_LOCAL_FILES |
|
||||
GW_MYSQL_CAPABILITIES_PLUGIN_AUTH |
|
||||
GW_MYSQL_CAPABILITIES_TRANSACTIONS |
|
||||
GW_MYSQL_CAPABILITIES_PROTOCOL_41 |
|
||||
GW_MYSQL_CAPABILITIES_MULTI_STATEMENTS |
|
||||
GW_MYSQL_CAPABILITIES_MULTI_RESULTS |
|
||||
GW_MYSQL_CAPABILITIES_PS_MULTI_RESULTS |
|
||||
GW_MYSQL_CAPABILITIES_COMPRESS
|
||||
),
|
||||
} gw_mysql_capabilities_t;
|
||||
|
||||
/* Basic mysql commands */
|
||||
#define MYSQL_COM_CHANGE_USER 0x11
|
||||
#define MYSQL_COM_QUIT 0x1
|
||||
#define MYSQL_COM_INIT_DB 0x2
|
||||
#define MYSQL_COM_QUERY 0x3
|
||||
|
||||
#define MYSQL_GET_COMMAND(payload) (payload[4])
|
||||
#define MYSQL_GET_PACKET_NO(payload) (payload[3])
|
||||
#define MYSQL_GET_PACKET_LEN(payload) (gw_mysql_get_byte3(payload))
|
||||
|
||||
#endif
|
||||
|
||||
void gw_mysql_close(MySQLProtocol **ptr);
|
||||
MySQLProtocol *gw_mysql_init(MySQLProtocol *data);
|
||||
void gw_mysql_close(MySQLProtocol **ptr);
|
||||
int gw_receive_backend_auth(MySQLProtocol *conn);
|
||||
int gw_decode_mysql_server_handshake(MySQLProtocol *conn, uint8_t *payload);
|
||||
int gw_read_backend_handshake(MySQLProtocol *conn);
|
||||
int gw_send_authentication_to_backend(char *dbname, char *user, uint8_t *passwd, MySQLProtocol *conn);
|
||||
const char *gw_mysql_protocol_state2string(int state);
|
||||
int gw_do_connect_to_backend(char *host, int port, MySQLProtocol *conn);
|
||||
int mysql_send_custom_error (DCB *dcb, int packet_number, int in_affected_rows, const char* mysql_message);
|
||||
int gw_send_change_user_to_backend(char *dbname, char *user, uint8_t *passwd, MySQLProtocol *conn);
|
||||
int gw_find_mysql_user_password_sha1(char *username, uint8_t *gateway_password, void *repository);
|
||||
int gw_check_mysql_scramble_data(DCB *dcb, uint8_t *token, unsigned int token_len, uint8_t *scramble, unsigned int scramble_len, char *username, uint8_t *stage1_hash);
|
||||
int mysql_send_auth_error (DCB *dcb, int packet_number, int in_affected_rows, const char* mysql_message);
|
||||
|
||||
extern void gw_sha1_str(const uint8_t *in, int in_len, uint8_t *out);
|
||||
extern void gw_sha1_2_str(const uint8_t *in, int in_len, const uint8_t *in2, int in2_len, uint8_t *out);
|
||||
extern void gw_str_xor(uint8_t *output, const uint8_t *input1, const uint8_t *input2, unsigned int len);
|
||||
extern char *gw_bin2hex(char *out, const uint8_t *in, unsigned int len);
|
||||
extern int gw_hex2bin(uint8_t *out, const char *in, unsigned int len);
|
||||
extern int gw_generate_random_str(char *output, int len);
|
||||
extern char *gw_strend(register const char *s);
|
||||
extern int setnonblocking(int fd);
|
||||
extern void setipaddress(struct in_addr *a, char *p);
|
||||
extern int gw_read_gwbuff(DCB *dcb, GWBUF **head, int b);
|
||||
76
server/modules/include/readconnection.h
Normal file
76
server/modules/include/readconnection.h
Normal file
@ -0,0 +1,76 @@
|
||||
#ifndef _READCONNECTION_H
|
||||
#define _READCONNECTION_H
|
||||
/*
|
||||
* This file is distributed as part of the SkySQL Gateway. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright SkySQL Ab 2013
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file readconnection.h - The read connection balancing query module heder file
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 14/06/13 Mark Riddoch Initial implementation
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
#include <dcb.h>
|
||||
|
||||
/**
|
||||
* Internal structure used to define the set of backend servers we are routing
|
||||
* connections to. This provides the storage for routing module specific data
|
||||
* that is required for each of the backend servers.
|
||||
*/
|
||||
typedef struct backend {
|
||||
SERVER *server; /**< The server itself */
|
||||
int count; /**< Number of connections to the server */
|
||||
} BACKEND;
|
||||
|
||||
/**
|
||||
* The client session structure used within this router.
|
||||
*/
|
||||
typedef struct client_session {
|
||||
BACKEND *backend; /**< Backend used by the client session */
|
||||
DCB *dcb; /**< DCB Connection to the backend */
|
||||
struct client_session
|
||||
*next;
|
||||
} CLIENT_SESSION;
|
||||
|
||||
/**
|
||||
* The statistics for this router instance
|
||||
*/
|
||||
typedef struct {
|
||||
int n_sessions; /**< Number sessions created */
|
||||
int n_queries; /**< Number of queries forwarded */
|
||||
} ROUTER_STATS;
|
||||
|
||||
|
||||
/**
|
||||
* The per instance data for the router.
|
||||
*/
|
||||
typedef struct instance {
|
||||
SERVICE *service; /**< Pointer to the service using this router */
|
||||
CLIENT_SESSION *connections; /**< Link list of all the client connections */
|
||||
SPINLOCK lock; /**< Spinlock for the instance data */
|
||||
BACKEND **servers; /**< The set of backend servers for this instance */
|
||||
unsigned int bitmask; /**< Bitmask to apply to server->status */
|
||||
unsigned int bitvalue; /**< Required value of server->status */
|
||||
ROUTER_STATS stats; /**< Statistics for this router */
|
||||
struct instance *next;
|
||||
} INSTANCE;
|
||||
#endif
|
||||
83
server/modules/include/readwritesplit.h
Normal file
83
server/modules/include/readwritesplit.h
Normal file
@ -0,0 +1,83 @@
|
||||
#ifndef _RWSPLITROUTER_H
|
||||
#define _RWSPLITROUTER_H
|
||||
/*
|
||||
* This file is distributed as part of the SkySQL Gateway. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright SkySQL Ab 2013
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file router.h - The read write split router module heder file
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Tässä mitään historioita..
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
#include <dcb.h>
|
||||
|
||||
typedef struct client_session CLIENT_SESSION;
|
||||
typedef struct instance INSTANCE;
|
||||
/**
|
||||
* Internal structure used to define the set of backend servers we are routing
|
||||
* connections to. This provides the storage for routing module specific data
|
||||
* that is required for each of the backend servers.
|
||||
*/
|
||||
typedef struct backend {
|
||||
SERVER* server; /**< The server itself */
|
||||
int count; /**< Number of connections to the server */
|
||||
} BACKEND;
|
||||
|
||||
/**
|
||||
* The client session structure used within this router.
|
||||
*/
|
||||
struct client_session {
|
||||
BACKEND* slave; /**< Slave used by the client session */
|
||||
BACKEND* master; /**< Master used by the client session */
|
||||
DCB* slaveconn; /**< Slave connection */
|
||||
DCB* masterconn; /**< Master connection */
|
||||
CLIENT_SESSION* next;
|
||||
};
|
||||
|
||||
/**
|
||||
* The statistics for this router instance
|
||||
*/
|
||||
typedef struct {
|
||||
int n_sessions; /**< Number sessions created */
|
||||
int n_queries; /**< Number of queries forwarded */
|
||||
int n_master; /**< Number of statements sent to master */
|
||||
int n_slave; /**< Number of statements sent to slave */
|
||||
int n_all; /**< Number of statements sent to all */
|
||||
} ROUTER_STATS;
|
||||
|
||||
|
||||
/**
|
||||
* The per instance data for the router.
|
||||
*/
|
||||
struct instance {
|
||||
SERVICE* service; /**< Pointer to the service using this router */
|
||||
CLIENT_SESSION* connections; /**< Link list of all the client connections */
|
||||
SPINLOCK lock; /**< Spinlock for the instance data */
|
||||
BACKEND** servers; /**< The set of backend servers for this instance */
|
||||
BACKEND* master; /**< NULL if not known, pointer otherwise */
|
||||
ROUTER_STATS stats; /**< Statistics for this router */
|
||||
INSTANCE* next;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
64
server/modules/include/telnetd.h
Normal file
64
server/modules/include/telnetd.h
Normal file
@ -0,0 +1,64 @@
|
||||
#ifndef _TELNETD_H
|
||||
#define _TELNETD_H
|
||||
/*
|
||||
* This file is distributed as part of the SkySQL Gateway. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright SkySQL Ab 2013
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file telnetd.h The telnetd protocol module header file
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 17/07/13 Mark Riddoch Initial implementation
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
#include <dcb.h>
|
||||
|
||||
/**
|
||||
* The telnetd specific protocol structure to put in the DCB.
|
||||
*/
|
||||
typedef struct telnetd {
|
||||
int state; /**< The connection state */
|
||||
char *username; /**< The login name of the user */
|
||||
} TELNETD;
|
||||
|
||||
#define TELNETD_STATE_LOGIN 1 /**< Issued login prompt */
|
||||
#define TELNETD_STATE_PASSWD 2 /**< Issued password prompt */
|
||||
#define TELNETD_STATE_DATA 3 /**< User logged in */
|
||||
|
||||
#define TELNET_SE 240
|
||||
#define TELNET_NOP 241
|
||||
#define TELNET_DATA_MARK 242
|
||||
#define TELNET_BRK 243
|
||||
#define TELNET_IP 244
|
||||
#define TELNET_AO 245
|
||||
#define TELNET_AYT 246
|
||||
#define TELNET_EC 247
|
||||
#define TELNET_EL 248
|
||||
#define TELNET_GA 249
|
||||
#define TELNET_SB 250
|
||||
#define TELNET_WILL 251
|
||||
#define TELNET_WONT 252
|
||||
#define TELNET_DO 253
|
||||
#define TELNET_DONT 254
|
||||
#define TELNET_IAC 255
|
||||
#define TELNET_ECHO 1
|
||||
#define TELNET_SUPPRESS_GO_AHEAD 3
|
||||
#endif
|
||||
70
server/modules/monitor/Makefile
Normal file
70
server/modules/monitor/Makefile
Normal file
@ -0,0 +1,70 @@
|
||||
# This file is distributed as part of the SkySQL Gateway. It is free
|
||||
# software: you can redistribute it and/or modify it under the terms of the
|
||||
# GNU General Public License as published by the Free Software Foundation,
|
||||
# version 2.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# Copyright SkySQL Ab 2013
|
||||
#
|
||||
# Revision History
|
||||
# Date Who Description
|
||||
# 08/07/13 Mark Riddoch Initial implementation
|
||||
|
||||
include ../../../build_gateway.inc
|
||||
LOGPATH := $(ROOT_PATH)/log_manager
|
||||
UTILSPATH := $(ROOT_PATH)/utils
|
||||
|
||||
CC=cc
|
||||
CFLAGS=-c -fPIC -I. -I/usr/include -I../include -I../../include -I$(LOGPATH) \
|
||||
-I$(UTILSPATH) -I$(MARIADB_SRC_PATH)/include/ -Wall -g
|
||||
|
||||
LDFLAGS=-shared -L$(LOGPATH) -Wl,-rpath,$(DEST)/lib \
|
||||
-Wl,-rpath,$(LOGPATH) -Wl,-rpath,$(UTILSPATH) \
|
||||
-Wl,-rpath,$(MARIADB_SRC_PATH)/libmysqld
|
||||
|
||||
|
||||
|
||||
MYSQLSRCS=mysql_mon.c
|
||||
MYSQLOBJ=$(MYSQLSRCS:.c=.o)
|
||||
GALERASRCS=mysql_mon.c
|
||||
GALERAOBJ=$(GALERASRCS:.c=.o)
|
||||
SRCS=$(MYSQLSRCS)
|
||||
OBJ=$(SRCS:.c=.o)
|
||||
LIBS=$(UTILSPATH)/skygw_utils.o -llog_manager \
|
||||
-L$(MARIADB_SRC_PATH)/libmysqld -lmysqld
|
||||
MODULES=libmysqlmon.so libgaleramon.so
|
||||
|
||||
|
||||
all: $(MODULES)
|
||||
|
||||
libmysqlmon.so: $(MYSQLOBJ)
|
||||
$(CC) $(LDFLAGS) $(MYSQLOBJ) $(LIBS) -o $@
|
||||
|
||||
libgaleramon.so: $(GALERAOBJ)
|
||||
$(CC) $(LDFLAGS) $(GALERAOBJ) $(LIBS) -o $@
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(OBJ) $(MODULES)
|
||||
|
||||
tags:
|
||||
ctags $(SRCS) $(HDRS)
|
||||
|
||||
depend:
|
||||
@rm -f depend.mk
|
||||
cc -M $(CFLAGS) $(SRCS) > depend.mk
|
||||
|
||||
install: $(MODULES)
|
||||
install -D $(MODULES) $(DEST)/MaxScale/modules
|
||||
|
||||
include depend.mk
|
||||
42
server/modules/monitor/depend.mk
Normal file
42
server/modules/monitor/depend.mk
Normal file
@ -0,0 +1,42 @@
|
||||
mysql_mon.o: mysql_mon.c /usr/include/stdio.h /usr/include/features.h \
|
||||
/usr/include/sys/cdefs.h /usr/include/bits/wordsize.h \
|
||||
/usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stddef.h \
|
||||
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
|
||||
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdarg.h \
|
||||
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
|
||||
/usr/include/stdlib.h /usr/include/bits/waitflags.h \
|
||||
/usr/include/bits/waitstatus.h /usr/include/endian.h \
|
||||
/usr/include/bits/endian.h /usr/include/bits/byteswap.h \
|
||||
/usr/include/sys/types.h /usr/include/time.h /usr/include/sys/select.h \
|
||||
/usr/include/bits/select.h /usr/include/bits/sigset.h \
|
||||
/usr/include/bits/time.h /usr/include/sys/sysmacros.h \
|
||||
/usr/include/bits/pthreadtypes.h /usr/include/alloca.h \
|
||||
/usr/include/string.h /usr/include/xlocale.h ../../include/monitor.h \
|
||||
../../include/server.h ../../include/dcb.h ../../include/spinlock.h \
|
||||
../../include/thread.h /usr/include/pthread.h /usr/include/sched.h \
|
||||
/usr/include/bits/sched.h /usr/include/bits/setjmp.h \
|
||||
../../include/buffer.h ../../include/gwbitmask.h mysqlmon.h \
|
||||
/usr/include/mysql/mysql.h /usr/include/mysql/mysql_version.h \
|
||||
/usr/include/mysql/mysql_com.h /usr/include/mysql/mysql_time.h \
|
||||
/usr/include/mysql/my_list.h /usr/include/mysql/typelib.h \
|
||||
/usr/include/mysql/my_alloc.h /usr/include/mysql/mysqld_error.h \
|
||||
/home/mriddoch/Repository/skygateway/utils/skygw_utils.h \
|
||||
/home/mriddoch/Repository/skygateway/utils/skygw_types.h \
|
||||
/usr/include/math.h /usr/include/bits/huge_val.h \
|
||||
/usr/include/bits/huge_valf.h /usr/include/bits/huge_vall.h \
|
||||
/usr/include/bits/inf.h /usr/include/bits/nan.h \
|
||||
/usr/include/bits/mathdef.h /usr/include/bits/mathcalls.h \
|
||||
/home/mriddoch/Repository/skygateway/utils/skygw_debug.h \
|
||||
/usr/include/assert.h /usr/include/unistd.h \
|
||||
/usr/include/bits/posix_opt.h /usr/include/bits/environments.h \
|
||||
/usr/include/bits/confname.h /usr/include/getopt.h \
|
||||
/home/mriddoch/Repository/skygateway/log_manager/log_manager.h \
|
||||
../../include/secrets.h /usr/include/sys/stat.h /usr/include/bits/stat.h \
|
||||
/usr/include/fcntl.h /usr/include/bits/fcntl.h /usr/include/errno.h \
|
||||
/usr/include/bits/errno.h /usr/include/linux/errno.h \
|
||||
/usr/include/asm/errno.h /usr/include/asm-generic/errno.h \
|
||||
/usr/include/asm-generic/errno-base.h /usr/include/openssl/aes.h \
|
||||
/usr/include/openssl/opensslconf.h \
|
||||
/usr/include/openssl/opensslconf-x86_64.h
|
||||
361
server/modules/monitor/galera_mon.c
Normal file
361
server/modules/monitor/galera_mon.c
Normal file
@ -0,0 +1,361 @@
|
||||
/*
|
||||
* This file is distributed as part of the SkySQL Gateway. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright SkySQL Ab 2013
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file galera_mon.c - A MySQL Galera cluster monitor
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 22/07/13 Mark Riddoch Initial implementation
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <monitor.h>
|
||||
#include <mysqlmon.h>
|
||||
#include <thread.h>
|
||||
#include <mysql.h>
|
||||
#include <mysqld_error.h>
|
||||
#include <skygw_utils.h>
|
||||
#include <log_manager.h>
|
||||
#include <secrets.h>
|
||||
#include <dcb.h>
|
||||
|
||||
static void monitorMain(void *);
|
||||
|
||||
static char *version_str = "V1.0.0";
|
||||
|
||||
static void *startMonitor(void *);
|
||||
static void stopMonitor(void *);
|
||||
static void registerServer(void *, SERVER *);
|
||||
static void unregisterServer(void *, SERVER *);
|
||||
static void defaultUsers(void *, char *, char *);
|
||||
static void daignostics(DCB *, void *);
|
||||
|
||||
static MONITOR_OBJECT MyObject = { startMonitor, stopMonitor, registerServer, unregisterServer, defaultUser, daignostics };
|
||||
|
||||
/**
|
||||
* Implementation of the mandatory version entry point
|
||||
*
|
||||
* @return version string of the module
|
||||
*/
|
||||
char *
|
||||
version()
|
||||
{
|
||||
return version_str;
|
||||
}
|
||||
|
||||
/**
|
||||
* The module initialisation routine, called when the module
|
||||
* is first loaded.
|
||||
*/
|
||||
void
|
||||
ModuleInit()
|
||||
{
|
||||
skygw_log_write(NULL, LOGFILE_MESSAGE, "Initialise the MySQL Galera Monitor module %s.\n",
|
||||
version_str);
|
||||
}
|
||||
|
||||
/**
|
||||
* The module entry point routine. It is this routine that
|
||||
* must populate the structure that is referred to as the
|
||||
* "module object", this is a structure with the set of
|
||||
* external entry points for this module.
|
||||
*
|
||||
* @return The module object
|
||||
*/
|
||||
MONITOR_OBJECT *
|
||||
GetModuleObject()
|
||||
{
|
||||
return &MyObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the instance of the monitor, returning a handle on the monitor.
|
||||
*
|
||||
* This function creates a thread to execute the actual monitoring.
|
||||
*
|
||||
* @return A handle to use when interacting with the monitor
|
||||
*/
|
||||
static void *
|
||||
startMonitor(void *arg)
|
||||
{
|
||||
MYSQL_MONITOR *handle;
|
||||
|
||||
if (arg != NULL)
|
||||
{
|
||||
handle = (MYSQL_MONITOR *)arg;
|
||||
handle->shutdown = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((handle = (MYSQL_MONITOR *)malloc(sizeof(MYSQL_MONITOR))) == NULL)
|
||||
return NULL;
|
||||
handle->databases = NULL;
|
||||
handle->shutdown = 0;
|
||||
handle->defaultUser = NULL;
|
||||
handle->deaultPasswd = NULL;
|
||||
spinlock_init(&handle->lock);
|
||||
}
|
||||
handle->tid = thread_start(monitorMain, handle);
|
||||
return handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop a running monitor
|
||||
*
|
||||
* @param arg Handle on thr running monior
|
||||
*/
|
||||
static void
|
||||
stopMonitor(void *arg)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
|
||||
handle->shutdown = 1;
|
||||
thread_wait(handle->tid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a server that must be added to the monitored servers for
|
||||
* a monitoring module.
|
||||
*
|
||||
* @param arg A handle on the running monitor module
|
||||
* @param server The server to add
|
||||
*/
|
||||
static void
|
||||
registerServer(void *arg, SERVER *server)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR_SERVERS *ptr, *db;
|
||||
|
||||
if ((db = (MONITOR_SERVERS *)malloc(sizeof(MONITOR_SERVERS))) == NULL)
|
||||
return;
|
||||
db->server = server;
|
||||
db->con = NULL;
|
||||
db->next = NULL;
|
||||
spinlock_acquire(&handle->lock);
|
||||
if (handle->databases == NULL)
|
||||
handle->databases = db;
|
||||
else
|
||||
{
|
||||
ptr = handle->databases;
|
||||
while (ptr->next != NULL)
|
||||
ptr = ptr->next;
|
||||
ptr->next = db;
|
||||
}
|
||||
spinlock_release(&handle->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a server from those being monitored by a monitoring module
|
||||
*
|
||||
* @param arg A handle on the running monitor module
|
||||
* @param server The server to remove
|
||||
*/
|
||||
static void
|
||||
unregisterServer(void *arg, SERVER *server)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR_SERVERS *ptr, *lptr;
|
||||
|
||||
spinlock_acquire(&handle->lock);
|
||||
if (handle->databases == NULL)
|
||||
{
|
||||
spinlock_release(&handle->lock);
|
||||
return;
|
||||
}
|
||||
if (handle->databases->server == server)
|
||||
{
|
||||
ptr = handle->databases;
|
||||
handle->databases = handle->databases->next;
|
||||
free(ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = handle->databases;
|
||||
while (ptr->next != NULL && ptr->next->server != server)
|
||||
ptr = ptr->next;
|
||||
if (ptr->next)
|
||||
{
|
||||
lptr = ptr->next;
|
||||
ptr->next = ptr->next->next;
|
||||
free(lptr);
|
||||
}
|
||||
}
|
||||
spinlock_release(&handle->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Diagnostic interface
|
||||
*
|
||||
* @param dcb DCB to send output
|
||||
* @param arg The monitor handle
|
||||
*/
|
||||
static void
|
||||
diagnostics(DCB *dcbm void *handle)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR_SERVERS *db;
|
||||
char *sep;
|
||||
|
||||
switch (handle->status)
|
||||
{
|
||||
case MONITOR_RUNNING:
|
||||
dcb_printf(dcb, "\tMonitor running\n");
|
||||
break;
|
||||
case MONITOR_STOPPING:
|
||||
dcb_printf(dcb, "\tMonitor stopping\n");
|
||||
break;
|
||||
case MONITOR_STOPPED:
|
||||
dcb_printf(dcb, "\tMonitor stopped\n");
|
||||
break;
|
||||
}
|
||||
dcb_printf(dcb, "\tMonitored servers: ");
|
||||
db = handle->databases;
|
||||
sep = "";
|
||||
while (db)
|
||||
{
|
||||
dcb_printf(dcb, "%s%s:%d", sep, db->server->name, db->server->port);
|
||||
sep = ", ";
|
||||
db = db->next;
|
||||
}
|
||||
dcb_printf(dcb, "\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default username and password to use to monitor if the server does not
|
||||
* override this.
|
||||
*
|
||||
* @param arg The handle allocated by startMonitor
|
||||
* @param uname The default user name
|
||||
* @param passwd The default password
|
||||
*/
|
||||
static void
|
||||
defaultUser(void *arg, char *uname, char *passwd)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
|
||||
if (handle->defaultUser)
|
||||
free(handle->defaultUser);
|
||||
if (handle->defaultPasswd)
|
||||
free(handle->defaultPasswd);
|
||||
handle->defaultUser = strdup(uname);
|
||||
handle->defaultPasswd = strdup(passwd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitor an individual server
|
||||
*
|
||||
* @param database The database to probe
|
||||
*/
|
||||
static void
|
||||
monitorDatabase(MONITOR_SERVERS *database, char *defaultUser, char *defaultPasswd)
|
||||
{
|
||||
MYSQL_ROW row;
|
||||
MYSQL_RES *result;
|
||||
int num_fields;
|
||||
int isjoined = 0;
|
||||
char *uname = defaultUser, *passwd = defaultPasswd;
|
||||
|
||||
if (database->server->monuser != NULL)
|
||||
{
|
||||
uname = database->server->monuser;
|
||||
passwd = database->server->monpw;
|
||||
}
|
||||
if (uname == NULL)
|
||||
return;
|
||||
|
||||
if (database->con == NULL || mysql_ping(database->con) != 0)
|
||||
{
|
||||
char *dpwd = decryptPassword(passwd);
|
||||
database->con = mysql_init(NULL);
|
||||
if (mysql_real_connect(database->con, database->server->name,
|
||||
uname, dpwd, NULL, database->server->port, NULL, 0) == NULL)
|
||||
{
|
||||
server_clear_status(database->server, SERVER_RUNNING);
|
||||
free(dpwd);
|
||||
return;
|
||||
}
|
||||
free(dpwd);
|
||||
}
|
||||
|
||||
/* If we get this far then we have a working connection */
|
||||
server_set_status(database->server, SERVER_RUNNING);
|
||||
|
||||
/* Check if the the Galera FSM shows this node is joined to the cluster */
|
||||
if (mysql_query(database->con, "SHOW STATUS LIKE 'wsrep_local_state_comment'") == 0
|
||||
&& (result = mysql_store_result(database->con)) != NULL)
|
||||
{
|
||||
num_fields = mysql_num_fields(result);
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
{
|
||||
if (strncasecmp(row[0], "JOINED", 3) == 0)
|
||||
isjoined = 1;
|
||||
}
|
||||
mysql_free_result(result);
|
||||
}
|
||||
|
||||
if (isjoined)
|
||||
server_set_status(database->server, SERVER_JOINED);
|
||||
else
|
||||
server_clear_status(database->server, SERVER_JOINED);
|
||||
}
|
||||
|
||||
/**
|
||||
* The entry point for the monitoring module thread
|
||||
*
|
||||
* @param arg The handle of the monitor
|
||||
*/
|
||||
static void
|
||||
monitorMain(void *arg)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR_SERVERS *ptr;
|
||||
|
||||
if (mysql_thread_init())
|
||||
{
|
||||
skygw_log_write_flush(NULL,
|
||||
LOGFILE_ERROR,
|
||||
"Fatal : mysql_init_thread failed in monitor "
|
||||
"module. Exiting.\n");
|
||||
return;
|
||||
}
|
||||
handle->status = MONITOR_RUNNING;
|
||||
while (1)
|
||||
{
|
||||
if (handle->shutdown)
|
||||
{
|
||||
handle->status = MONITOR_STOPPING;
|
||||
mysql_thread_end();
|
||||
handle->status = MONITOR_STOPPED;
|
||||
return;
|
||||
}
|
||||
ptr = handle->databases;
|
||||
while (ptr)
|
||||
{
|
||||
monitorDatabase(ptr, handle->defaultUser, handle->defaultPasswd);
|
||||
ptr = ptr->next;
|
||||
}
|
||||
thread_millisleep(10000);
|
||||
}
|
||||
}
|
||||
399
server/modules/monitor/mysql_mon.c
Normal file
399
server/modules/monitor/mysql_mon.c
Normal file
@ -0,0 +1,399 @@
|
||||
/*
|
||||
* This file is distributed as part of the SkySQL Gateway. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright SkySQL Ab 2013
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file mysql_mon.c - A MySQL replication cluster monitor
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 08/07/13 Mark Riddoch Initial implementation
|
||||
* 11/07/13 Mark Riddoch Addition of code to check replication
|
||||
* status
|
||||
* 25/07/13 Mark Riddoch Addition of decrypt for passwords and
|
||||
* diagnostic interface
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <monitor.h>
|
||||
#include <mysqlmon.h>
|
||||
#include <thread.h>
|
||||
#include <mysql.h>
|
||||
#include <mysqld_error.h>
|
||||
#include <skygw_utils.h>
|
||||
#include <log_manager.h>
|
||||
#include <secrets.h>
|
||||
#include <dcb.h>
|
||||
|
||||
static void monitorMain(void *);
|
||||
|
||||
static char *version_str = "V1.0.0";
|
||||
|
||||
static void *startMonitor(void *);
|
||||
static void stopMonitor(void *);
|
||||
static void registerServer(void *, SERVER *);
|
||||
static void unregisterServer(void *, SERVER *);
|
||||
static void defaultUser(void *, char *, char *);
|
||||
static void diagnostics(DCB *, void *);
|
||||
|
||||
static MONITOR_OBJECT MyObject = { startMonitor, stopMonitor, registerServer, unregisterServer, defaultUser, diagnostics };
|
||||
|
||||
/**
|
||||
* Implementation of the mandatory version entry point
|
||||
*
|
||||
* @return version string of the module
|
||||
*/
|
||||
char *
|
||||
version()
|
||||
{
|
||||
return version_str;
|
||||
}
|
||||
|
||||
/**
|
||||
* The module initialisation routine, called when the module
|
||||
* is first loaded.
|
||||
*/
|
||||
void
|
||||
ModuleInit()
|
||||
{
|
||||
skygw_log_write(NULL, LOGFILE_MESSAGE, "Initialise the MySQL Monitor module %s.\n",
|
||||
version_str);
|
||||
}
|
||||
|
||||
/**
|
||||
* The module entry point routine. It is this routine that
|
||||
* must populate the structure that is referred to as the
|
||||
* "module object", this is a structure with the set of
|
||||
* external entry points for this module.
|
||||
*
|
||||
* @return The module object
|
||||
*/
|
||||
MONITOR_OBJECT *
|
||||
GetModuleObject()
|
||||
{
|
||||
return &MyObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the instance of the monitor, returning a handle on the monitor.
|
||||
*
|
||||
* This function creates a thread to execute the actual monitoring.
|
||||
*
|
||||
* @param arg The current handle - NULL if first start
|
||||
* @return A handle to use when interacting with the monitor
|
||||
*/
|
||||
static void *
|
||||
startMonitor(void *arg)
|
||||
{
|
||||
MYSQL_MONITOR *handle;
|
||||
|
||||
if (arg)
|
||||
{
|
||||
handle = arg; /* Must be a restart */
|
||||
handle->shutdown = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((handle = (MYSQL_MONITOR *)malloc(sizeof(MYSQL_MONITOR))) == NULL)
|
||||
return NULL;
|
||||
handle->databases = NULL;
|
||||
handle->shutdown = 0;
|
||||
handle->defaultUser = NULL;
|
||||
handle->defaultPasswd = NULL;
|
||||
spinlock_init(&handle->lock);
|
||||
}
|
||||
handle->tid = thread_start(monitorMain, handle);
|
||||
return handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop a running monitor
|
||||
*
|
||||
* @param arg Handle on thr running monior
|
||||
*/
|
||||
static void
|
||||
stopMonitor(void *arg)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
|
||||
handle->shutdown = 1;
|
||||
thread_wait(handle->tid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a server that must be added to the monitored servers for
|
||||
* a monitoring module.
|
||||
*
|
||||
* @param arg A handle on the running monitor module
|
||||
* @param server The server to add
|
||||
*/
|
||||
static void
|
||||
registerServer(void *arg, SERVER *server)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR_SERVERS *ptr, *db;
|
||||
|
||||
if ((db = (MONITOR_SERVERS *)malloc(sizeof(MONITOR_SERVERS))) == NULL)
|
||||
return;
|
||||
db->server = server;
|
||||
db->con = NULL;
|
||||
db->next = NULL;
|
||||
spinlock_acquire(&handle->lock);
|
||||
if (handle->databases == NULL)
|
||||
handle->databases = db;
|
||||
else
|
||||
{
|
||||
ptr = handle->databases;
|
||||
while (ptr->next != NULL)
|
||||
ptr = ptr->next;
|
||||
ptr->next = db;
|
||||
}
|
||||
spinlock_release(&handle->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a server from those being monitored by a monitoring module
|
||||
*
|
||||
* @param arg A handle on the running monitor module
|
||||
* @param server The server to remove
|
||||
*/
|
||||
static void
|
||||
unregisterServer(void *arg, SERVER *server)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR_SERVERS *ptr, *lptr;
|
||||
|
||||
spinlock_acquire(&handle->lock);
|
||||
if (handle->databases == NULL)
|
||||
{
|
||||
spinlock_release(&handle->lock);
|
||||
return;
|
||||
}
|
||||
if (handle->databases->server == server)
|
||||
{
|
||||
ptr = handle->databases;
|
||||
handle->databases = handle->databases->next;
|
||||
free(ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = handle->databases;
|
||||
while (ptr->next != NULL && ptr->next->server != server)
|
||||
ptr = ptr->next;
|
||||
if (ptr->next)
|
||||
{
|
||||
lptr = ptr->next;
|
||||
ptr->next = ptr->next->next;
|
||||
free(lptr);
|
||||
}
|
||||
}
|
||||
spinlock_release(&handle->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default username and password to use to monitor if the server does not
|
||||
* override this.
|
||||
*
|
||||
* @param arg The handle allocated by startMonitor
|
||||
* @param uname The default user name
|
||||
* @param passwd The default password
|
||||
*/
|
||||
static void
|
||||
defaultUser(void *arg, char *uname, char *passwd)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
|
||||
if (handle->defaultUser)
|
||||
free(handle->defaultUser);
|
||||
if (handle->defaultPasswd)
|
||||
free(handle->defaultPasswd);
|
||||
handle->defaultUser = strdup(uname);
|
||||
handle->defaultPasswd = strdup(passwd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Daignostic interface
|
||||
*
|
||||
* @param dcb DCB to print diagnostics
|
||||
* @param arg The monitor handle
|
||||
*/
|
||||
static void diagnostics(DCB *dcb, void *arg)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR_SERVERS *db;
|
||||
char *sep;
|
||||
|
||||
switch (handle->status)
|
||||
{
|
||||
case MONITOR_RUNNING:
|
||||
dcb_printf(dcb, "\tMonitor running\n");
|
||||
break;
|
||||
case MONITOR_STOPPING:
|
||||
dcb_printf(dcb, "\tMonitor stopping\n");
|
||||
break;
|
||||
case MONITOR_STOPPED:
|
||||
dcb_printf(dcb, "\tMonitor stopped\n");
|
||||
break;
|
||||
}
|
||||
dcb_printf(dcb, "\tMonitored servers: ");
|
||||
db = handle->databases;
|
||||
sep = "";
|
||||
while (db)
|
||||
{
|
||||
dcb_printf(dcb, "%s%s:%d", sep, db->server->name, db->server->port);
|
||||
sep = ", ";
|
||||
db = db->next;
|
||||
}
|
||||
dcb_printf(dcb, "\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitor an individual server
|
||||
*
|
||||
* @param database The database to probe
|
||||
* @param defaultUser Default username for the monitor
|
||||
* @param defaultPasswd Default password for the monitor
|
||||
*/
|
||||
static void
|
||||
monitorDatabase(MONITOR_SERVERS *database, char *defaultUser, char *defaultPasswd)
|
||||
{
|
||||
MYSQL_ROW row;
|
||||
MYSQL_RES *result;
|
||||
int num_fields;
|
||||
int ismaster = 0, isslave = 0;
|
||||
char *uname = defaultUser, *passwd = defaultPasswd;
|
||||
|
||||
if (database->server->monuser != NULL)
|
||||
{
|
||||
uname = database->server->monuser;
|
||||
passwd = database->server->monpw;
|
||||
}
|
||||
if (uname == NULL)
|
||||
return;
|
||||
if (database->con == NULL || mysql_ping(database->con) != 0)
|
||||
{
|
||||
char *dpwd = decryptPassword(passwd);
|
||||
database->con = mysql_init(NULL);
|
||||
if (mysql_real_connect(database->con, database->server->name,
|
||||
uname, dpwd, NULL, database->server->port, NULL, 0) == NULL)
|
||||
{
|
||||
free(dpwd);
|
||||
server_clear_status(database->server, SERVER_RUNNING);
|
||||
return;
|
||||
}
|
||||
free(dpwd);
|
||||
}
|
||||
|
||||
/* If we get this far then we have a working connection */
|
||||
server_set_status(database->server, SERVER_RUNNING);
|
||||
|
||||
/* Check SHOW SLAVE HOSTS - if we get rows then we are a master */
|
||||
if (mysql_query(database->con, "SHOW SLAVE HOSTS"))
|
||||
{
|
||||
if (mysql_errno(database->con) == ER_SPECIFIC_ACCESS_DENIED_ERROR)
|
||||
{
|
||||
/* Log lack of permission */
|
||||
}
|
||||
}
|
||||
else if ((result = mysql_store_result(database->con)) != NULL)
|
||||
{
|
||||
num_fields = mysql_num_fields(result);
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
{
|
||||
ismaster = 1;
|
||||
}
|
||||
mysql_free_result(result);
|
||||
}
|
||||
|
||||
/* Check if the Slave_SQL_Running and Slave_IO_Running status is
|
||||
* set to Yes
|
||||
*/
|
||||
if (mysql_query(database->con, "SHOW SLAVE STATUS") == 0
|
||||
&& (result = mysql_store_result(database->con)) != NULL)
|
||||
{
|
||||
num_fields = mysql_num_fields(result);
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
{
|
||||
if (strncmp(row[10], "Yes", 3) == 0
|
||||
&& strncmp(row[11], "Yes", 3) == 0)
|
||||
isslave = 1;
|
||||
}
|
||||
mysql_free_result(result);
|
||||
}
|
||||
|
||||
if (ismaster)
|
||||
{
|
||||
server_set_status(database->server, SERVER_MASTER);
|
||||
server_clear_status(database->server, SERVER_SLAVE);
|
||||
}
|
||||
else if (isslave)
|
||||
{
|
||||
server_set_status(database->server, SERVER_SLAVE);
|
||||
server_clear_status(database->server, SERVER_MASTER);
|
||||
}
|
||||
if (ismaster == 0 && isslave == 0)
|
||||
{
|
||||
server_clear_status(database->server, SERVER_SLAVE);
|
||||
server_clear_status(database->server, SERVER_MASTER);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The entry point for the monitoring module thread
|
||||
*
|
||||
* @param arg The handle of the monitor
|
||||
*/
|
||||
static void
|
||||
monitorMain(void *arg)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR_SERVERS *ptr;
|
||||
|
||||
if (mysql_thread_init())
|
||||
{
|
||||
skygw_log_write_flush(NULL,
|
||||
LOGFILE_ERROR,
|
||||
"Fatal : mysql_init_thread failed in monitor "
|
||||
"module. Exiting.\n");
|
||||
return;
|
||||
}
|
||||
handle->status = MONITOR_RUNNING;
|
||||
while (1)
|
||||
{
|
||||
if (handle->shutdown)
|
||||
{
|
||||
handle->status = MONITOR_STOPPING;
|
||||
mysql_thread_end();
|
||||
handle->status = MONITOR_STOPPED;
|
||||
return;
|
||||
}
|
||||
ptr = handle->databases;
|
||||
while (ptr)
|
||||
{
|
||||
monitorDatabase(ptr, handle->defaultUser, handle->defaultPasswd);
|
||||
ptr = ptr->next;
|
||||
}
|
||||
thread_millisleep(10000);
|
||||
}
|
||||
}
|
||||
64
server/modules/monitor/mysqlmon.h
Normal file
64
server/modules/monitor/mysqlmon.h
Normal file
@ -0,0 +1,64 @@
|
||||
#ifndef _MYSQLMON_H
|
||||
#define _MYSQLMON_H
|
||||
/*
|
||||
* This file is distributed as part of the SkySQL Gateway. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright SkySQL Ab 2013
|
||||
*/
|
||||
#include <server.h>
|
||||
#include <spinlock.h>
|
||||
#include <mysql.h>
|
||||
|
||||
/**
|
||||
* @file mysqlmon.h - The MySQL monitor functionality within the gateway
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 08/07/13 Mark Riddoch Initial implementation
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
/**
|
||||
* The linked list of servers that are being monitored by the MySQL
|
||||
* Monitor module.
|
||||
*/
|
||||
typedef struct monitor_servers {
|
||||
SERVER *server; /**< The server being monitored */
|
||||
MYSQL *con; /**< The MySQL connection */
|
||||
struct monitor_servers
|
||||
*next; /**< The next server in the list */
|
||||
} MONITOR_SERVERS;
|
||||
|
||||
/**
|
||||
* The handle for an instance of a MySQL Monitor module
|
||||
*/
|
||||
typedef struct {
|
||||
SPINLOCK lock; /**< The monitor spinlock */
|
||||
pthread_t tid; /**< id of monitor thread */
|
||||
int shutdown; /**< Flag to shutdown the monitor thread */
|
||||
int status; /**< Monitor status */
|
||||
char *defaultUser; /**< Default username for monitoring */
|
||||
char *defaultPasswd; /**< Default password for monitoring */
|
||||
MONITOR_SERVERS *databases; /**< Linked list of servers to monitor */
|
||||
} MYSQL_MONITOR;
|
||||
|
||||
#define MONITOR_RUNNING 1
|
||||
#define MONITOR_STOPPING 2
|
||||
#define MONITOR_STOPPED 3
|
||||
|
||||
#endif
|
||||
83
server/modules/protocol/Makefile
Normal file
83
server/modules/protocol/Makefile
Normal file
@ -0,0 +1,83 @@
|
||||
# This file is distributed as part of the SkySQL Gateway. It is free
|
||||
# software: you can redistribute it and/or modify it under the terms of the
|
||||
# GNU General Public License as published by the Free Software Foundation,
|
||||
# version 2.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# Copyright SkySQL Ab 2013
|
||||
#
|
||||
# Revision History
|
||||
# Date Who Description
|
||||
# 13/06/2013 Mark Riddoch Initial protocol module development
|
||||
# 17/06/2013 Massimiliano Pinto Added mysql_common top both libraries
|
||||
# 27/06/2013 Vilho Raatikka Added logmanager-related libs and
|
||||
# headers so that liblog_manager.so can
|
||||
# be linked in.
|
||||
# 09/07/2013 Massimiliano Pinto Added the HTTPD protocol module
|
||||
#
|
||||
include ../../../build_gateway.inc
|
||||
|
||||
LOGPATH := $(ROOT_PATH)/log_manager
|
||||
UTILSPATH := $(ROOT_PATH)/utils
|
||||
|
||||
CC=cc
|
||||
CFLAGS=-c -fPIC -I/usr/include -I../include -I../../include -I$(LOGPATH) \
|
||||
-I$(UTILSPATH) -Wall -g
|
||||
|
||||
include ../../../makefile.inc
|
||||
|
||||
LDFLAGS=-shared -L$(LOGPATH) -Wl,-rpath,$(DEST)/lib \
|
||||
-Wl,-rpath,$(LOGPATH) -Wl,-rpath,$(UTILSPATH)
|
||||
|
||||
MYSQLCLIENTSRCS=mysql_client.c mysql_common.c
|
||||
MYSQLCLIENTOBJ=$(MYSQLCLIENTSRCS:.c=.o)
|
||||
MYSQLBACKENDSRCS=mysql_backend.c mysql_common.c
|
||||
MYSQLBACKENDOBJ=$(MYSQLBACKENDSRCS:.c=.o)
|
||||
TELNETDSRCS=telnetd.c
|
||||
TELNETDOBJ=$(TELNETDSRCS:.c=.o)
|
||||
HTTPDSRCS=httpd.c
|
||||
HTTPDOBJ=$(HTTPDSRCS:.c=.o)
|
||||
SRCS=$(MYSQLCLIENTSRCS) $(MYSQLBACKENDSRCS) $(TELNETDSRCS) $(HTTPDSRCS)
|
||||
OBJ=$(SRCS:.c=.o)
|
||||
LIBS=$(UTILSPATH)/skygw_utils.o
|
||||
MODULES=libMySQLClient.so libMySQLBackend.so libtelnetd.so libHTTPD.so
|
||||
|
||||
all: $(MODULES)
|
||||
|
||||
libMySQLClient.so: $(MYSQLCLIENTOBJ)
|
||||
$(CC) $(LDFLAGS) $(MYSQLCLIENTOBJ) $(LIBS) -o $@
|
||||
|
||||
libMySQLBackend.so: $(MYSQLBACKENDOBJ)
|
||||
$(CC) $(LDFLAGS) $(MYSQLBACKENDOBJ) $(LIBS) -o $@
|
||||
|
||||
libtelnetd.so: $(TELNETDOBJ)
|
||||
$(CC) $(LDFLAGS) $(TELNETDOBJ) $(LIBS) -lcrypt -o $@
|
||||
|
||||
libHTTPD.so: $(HTTPDOBJ)
|
||||
$(CC) $(LDFLAGS) $(HTTPDOBJ) $(LIBS) -o $@
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(OBJ) $(MODULES)
|
||||
|
||||
tags:
|
||||
ctags $(SRCS) $(HDRS)
|
||||
|
||||
install: $(MODULES)
|
||||
install -D $(MODULES) $(DEST)/MaxScale/modules
|
||||
|
||||
depend:
|
||||
rm -f depend.mk
|
||||
cc -M $(CFLAGS) $(SRCS) > depend.mk
|
||||
|
||||
include depend.mk
|
||||
274
server/modules/protocol/depend.mk
Normal file
274
server/modules/protocol/depend.mk
Normal file
@ -0,0 +1,274 @@
|
||||
mysql_client.o: mysql_client.c ../include/mysql_client_server_protocol.h \
|
||||
/usr/include/stdio.h /usr/include/features.h /usr/include/sys/cdefs.h \
|
||||
/usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
|
||||
/usr/include/gnu/stubs-64.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stddef.h \
|
||||
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
|
||||
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdarg.h \
|
||||
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
|
||||
/usr/include/stdlib.h /usr/include/bits/waitflags.h \
|
||||
/usr/include/bits/waitstatus.h /usr/include/endian.h \
|
||||
/usr/include/bits/endian.h /usr/include/bits/byteswap.h \
|
||||
/usr/include/sys/types.h /usr/include/time.h /usr/include/sys/select.h \
|
||||
/usr/include/bits/select.h /usr/include/bits/sigset.h \
|
||||
/usr/include/bits/time.h /usr/include/sys/sysmacros.h \
|
||||
/usr/include/bits/pthreadtypes.h /usr/include/alloca.h \
|
||||
/usr/include/stdint.h /usr/include/bits/wchar.h /usr/include/string.h \
|
||||
/usr/include/xlocale.h /usr/include/openssl/sha.h \
|
||||
/usr/include/openssl/e_os2.h /usr/include/openssl/opensslconf.h \
|
||||
/usr/include/openssl/opensslconf-x86_64.h /usr/include/sys/ioctl.h \
|
||||
/usr/include/bits/ioctls.h /usr/include/asm/ioctls.h \
|
||||
/usr/include/asm-generic/ioctls.h /usr/include/linux/ioctl.h \
|
||||
/usr/include/asm/ioctl.h /usr/include/asm-generic/ioctl.h \
|
||||
/usr/include/bits/ioctl-types.h /usr/include/sys/ttydefaults.h \
|
||||
/usr/include/errno.h /usr/include/bits/errno.h \
|
||||
/usr/include/linux/errno.h /usr/include/asm/errno.h \
|
||||
/usr/include/asm-generic/errno.h /usr/include/asm-generic/errno-base.h \
|
||||
/usr/include/sys/socket.h /usr/include/sys/uio.h /usr/include/bits/uio.h \
|
||||
/usr/include/bits/socket.h /usr/include/bits/sockaddr.h \
|
||||
/usr/include/asm/socket.h /usr/include/asm-generic/socket.h \
|
||||
/usr/include/asm/sockios.h /usr/include/asm-generic/sockios.h \
|
||||
/usr/include/netinet/in.h /usr/include/bits/in.h \
|
||||
/usr/include/arpa/inet.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdbool.h \
|
||||
/usr/include/fcntl.h /usr/include/bits/fcntl.h /usr/include/bits/stat.h \
|
||||
/usr/include/unistd.h /usr/include/bits/posix_opt.h \
|
||||
/usr/include/bits/environments.h /usr/include/bits/confname.h \
|
||||
/usr/include/getopt.h ../../include/service.h ../../include/spinlock.h \
|
||||
../../include/thread.h /usr/include/pthread.h /usr/include/sched.h \
|
||||
/usr/include/bits/sched.h /usr/include/bits/setjmp.h ../../include/dcb.h \
|
||||
../../include/buffer.h ../../include/gwbitmask.h ../../include/server.h \
|
||||
../../include/router.h ../../include/session.h ../../include/poll.h \
|
||||
../../include/users.h ../../include/hashtable.h ../../include/atomic.h
|
||||
mysql_common.o: mysql_common.c ../include/mysql_client_server_protocol.h \
|
||||
/usr/include/stdio.h /usr/include/features.h /usr/include/sys/cdefs.h \
|
||||
/usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
|
||||
/usr/include/gnu/stubs-64.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stddef.h \
|
||||
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
|
||||
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdarg.h \
|
||||
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
|
||||
/usr/include/stdlib.h /usr/include/bits/waitflags.h \
|
||||
/usr/include/bits/waitstatus.h /usr/include/endian.h \
|
||||
/usr/include/bits/endian.h /usr/include/bits/byteswap.h \
|
||||
/usr/include/sys/types.h /usr/include/time.h /usr/include/sys/select.h \
|
||||
/usr/include/bits/select.h /usr/include/bits/sigset.h \
|
||||
/usr/include/bits/time.h /usr/include/sys/sysmacros.h \
|
||||
/usr/include/bits/pthreadtypes.h /usr/include/alloca.h \
|
||||
/usr/include/stdint.h /usr/include/bits/wchar.h /usr/include/string.h \
|
||||
/usr/include/xlocale.h /usr/include/openssl/sha.h \
|
||||
/usr/include/openssl/e_os2.h /usr/include/openssl/opensslconf.h \
|
||||
/usr/include/openssl/opensslconf-x86_64.h /usr/include/sys/ioctl.h \
|
||||
/usr/include/bits/ioctls.h /usr/include/asm/ioctls.h \
|
||||
/usr/include/asm-generic/ioctls.h /usr/include/linux/ioctl.h \
|
||||
/usr/include/asm/ioctl.h /usr/include/asm-generic/ioctl.h \
|
||||
/usr/include/bits/ioctl-types.h /usr/include/sys/ttydefaults.h \
|
||||
/usr/include/errno.h /usr/include/bits/errno.h \
|
||||
/usr/include/linux/errno.h /usr/include/asm/errno.h \
|
||||
/usr/include/asm-generic/errno.h /usr/include/asm-generic/errno-base.h \
|
||||
/usr/include/sys/socket.h /usr/include/sys/uio.h /usr/include/bits/uio.h \
|
||||
/usr/include/bits/socket.h /usr/include/bits/sockaddr.h \
|
||||
/usr/include/asm/socket.h /usr/include/asm-generic/socket.h \
|
||||
/usr/include/asm/sockios.h /usr/include/asm-generic/sockios.h \
|
||||
/usr/include/netinet/in.h /usr/include/bits/in.h \
|
||||
/usr/include/arpa/inet.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdbool.h \
|
||||
/usr/include/fcntl.h /usr/include/bits/fcntl.h /usr/include/bits/stat.h \
|
||||
/usr/include/unistd.h /usr/include/bits/posix_opt.h \
|
||||
/usr/include/bits/environments.h /usr/include/bits/confname.h \
|
||||
/usr/include/getopt.h ../../include/service.h ../../include/spinlock.h \
|
||||
../../include/thread.h /usr/include/pthread.h /usr/include/sched.h \
|
||||
/usr/include/bits/sched.h /usr/include/bits/setjmp.h ../../include/dcb.h \
|
||||
../../include/buffer.h ../../include/gwbitmask.h ../../include/server.h \
|
||||
../../include/router.h ../../include/session.h ../../include/poll.h \
|
||||
../../include/users.h ../../include/hashtable.h ../../include/atomic.h
|
||||
mysql_backend.o: mysql_backend.c \
|
||||
../include/mysql_client_server_protocol.h /usr/include/stdio.h \
|
||||
/usr/include/features.h /usr/include/sys/cdefs.h \
|
||||
/usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
|
||||
/usr/include/gnu/stubs-64.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stddef.h \
|
||||
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
|
||||
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdarg.h \
|
||||
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
|
||||
/usr/include/stdlib.h /usr/include/bits/waitflags.h \
|
||||
/usr/include/bits/waitstatus.h /usr/include/endian.h \
|
||||
/usr/include/bits/endian.h /usr/include/bits/byteswap.h \
|
||||
/usr/include/sys/types.h /usr/include/time.h /usr/include/sys/select.h \
|
||||
/usr/include/bits/select.h /usr/include/bits/sigset.h \
|
||||
/usr/include/bits/time.h /usr/include/sys/sysmacros.h \
|
||||
/usr/include/bits/pthreadtypes.h /usr/include/alloca.h \
|
||||
/usr/include/stdint.h /usr/include/bits/wchar.h /usr/include/string.h \
|
||||
/usr/include/xlocale.h /usr/include/openssl/sha.h \
|
||||
/usr/include/openssl/e_os2.h /usr/include/openssl/opensslconf.h \
|
||||
/usr/include/openssl/opensslconf-x86_64.h /usr/include/sys/ioctl.h \
|
||||
/usr/include/bits/ioctls.h /usr/include/asm/ioctls.h \
|
||||
/usr/include/asm-generic/ioctls.h /usr/include/linux/ioctl.h \
|
||||
/usr/include/asm/ioctl.h /usr/include/asm-generic/ioctl.h \
|
||||
/usr/include/bits/ioctl-types.h /usr/include/sys/ttydefaults.h \
|
||||
/usr/include/errno.h /usr/include/bits/errno.h \
|
||||
/usr/include/linux/errno.h /usr/include/asm/errno.h \
|
||||
/usr/include/asm-generic/errno.h /usr/include/asm-generic/errno-base.h \
|
||||
/usr/include/sys/socket.h /usr/include/sys/uio.h /usr/include/bits/uio.h \
|
||||
/usr/include/bits/socket.h /usr/include/bits/sockaddr.h \
|
||||
/usr/include/asm/socket.h /usr/include/asm-generic/socket.h \
|
||||
/usr/include/asm/sockios.h /usr/include/asm-generic/sockios.h \
|
||||
/usr/include/netinet/in.h /usr/include/bits/in.h \
|
||||
/usr/include/arpa/inet.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdbool.h \
|
||||
/usr/include/fcntl.h /usr/include/bits/fcntl.h /usr/include/bits/stat.h \
|
||||
/usr/include/unistd.h /usr/include/bits/posix_opt.h \
|
||||
/usr/include/bits/environments.h /usr/include/bits/confname.h \
|
||||
/usr/include/getopt.h ../../include/service.h ../../include/spinlock.h \
|
||||
../../include/thread.h /usr/include/pthread.h /usr/include/sched.h \
|
||||
/usr/include/bits/sched.h /usr/include/bits/setjmp.h ../../include/dcb.h \
|
||||
../../include/buffer.h ../../include/gwbitmask.h ../../include/server.h \
|
||||
../../include/router.h ../../include/session.h ../../include/poll.h \
|
||||
../../include/users.h ../../include/hashtable.h ../../include/atomic.h
|
||||
mysql_common.o: mysql_common.c ../include/mysql_client_server_protocol.h \
|
||||
/usr/include/stdio.h /usr/include/features.h /usr/include/sys/cdefs.h \
|
||||
/usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
|
||||
/usr/include/gnu/stubs-64.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stddef.h \
|
||||
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
|
||||
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdarg.h \
|
||||
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
|
||||
/usr/include/stdlib.h /usr/include/bits/waitflags.h \
|
||||
/usr/include/bits/waitstatus.h /usr/include/endian.h \
|
||||
/usr/include/bits/endian.h /usr/include/bits/byteswap.h \
|
||||
/usr/include/sys/types.h /usr/include/time.h /usr/include/sys/select.h \
|
||||
/usr/include/bits/select.h /usr/include/bits/sigset.h \
|
||||
/usr/include/bits/time.h /usr/include/sys/sysmacros.h \
|
||||
/usr/include/bits/pthreadtypes.h /usr/include/alloca.h \
|
||||
/usr/include/stdint.h /usr/include/bits/wchar.h /usr/include/string.h \
|
||||
/usr/include/xlocale.h /usr/include/openssl/sha.h \
|
||||
/usr/include/openssl/e_os2.h /usr/include/openssl/opensslconf.h \
|
||||
/usr/include/openssl/opensslconf-x86_64.h /usr/include/sys/ioctl.h \
|
||||
/usr/include/bits/ioctls.h /usr/include/asm/ioctls.h \
|
||||
/usr/include/asm-generic/ioctls.h /usr/include/linux/ioctl.h \
|
||||
/usr/include/asm/ioctl.h /usr/include/asm-generic/ioctl.h \
|
||||
/usr/include/bits/ioctl-types.h /usr/include/sys/ttydefaults.h \
|
||||
/usr/include/errno.h /usr/include/bits/errno.h \
|
||||
/usr/include/linux/errno.h /usr/include/asm/errno.h \
|
||||
/usr/include/asm-generic/errno.h /usr/include/asm-generic/errno-base.h \
|
||||
/usr/include/sys/socket.h /usr/include/sys/uio.h /usr/include/bits/uio.h \
|
||||
/usr/include/bits/socket.h /usr/include/bits/sockaddr.h \
|
||||
/usr/include/asm/socket.h /usr/include/asm-generic/socket.h \
|
||||
/usr/include/asm/sockios.h /usr/include/asm-generic/sockios.h \
|
||||
/usr/include/netinet/in.h /usr/include/bits/in.h \
|
||||
/usr/include/arpa/inet.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdbool.h \
|
||||
/usr/include/fcntl.h /usr/include/bits/fcntl.h /usr/include/bits/stat.h \
|
||||
/usr/include/unistd.h /usr/include/bits/posix_opt.h \
|
||||
/usr/include/bits/environments.h /usr/include/bits/confname.h \
|
||||
/usr/include/getopt.h ../../include/service.h ../../include/spinlock.h \
|
||||
../../include/thread.h /usr/include/pthread.h /usr/include/sched.h \
|
||||
/usr/include/bits/sched.h /usr/include/bits/setjmp.h ../../include/dcb.h \
|
||||
../../include/buffer.h ../../include/gwbitmask.h ../../include/server.h \
|
||||
../../include/router.h ../../include/session.h ../../include/poll.h \
|
||||
../../include/users.h ../../include/hashtable.h ../../include/atomic.h
|
||||
telnetd.o: telnetd.c /usr/include/stdio.h /usr/include/features.h \
|
||||
/usr/include/sys/cdefs.h /usr/include/bits/wordsize.h \
|
||||
/usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stddef.h \
|
||||
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
|
||||
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdarg.h \
|
||||
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
|
||||
/usr/include/stdlib.h /usr/include/bits/waitflags.h \
|
||||
/usr/include/bits/waitstatus.h /usr/include/endian.h \
|
||||
/usr/include/bits/endian.h /usr/include/bits/byteswap.h \
|
||||
/usr/include/sys/types.h /usr/include/time.h /usr/include/sys/select.h \
|
||||
/usr/include/bits/select.h /usr/include/bits/sigset.h \
|
||||
/usr/include/bits/time.h /usr/include/sys/sysmacros.h \
|
||||
/usr/include/bits/pthreadtypes.h /usr/include/alloca.h \
|
||||
/usr/include/unistd.h /usr/include/bits/posix_opt.h \
|
||||
/usr/include/bits/environments.h /usr/include/bits/confname.h \
|
||||
/usr/include/getopt.h /usr/include/string.h /usr/include/xlocale.h \
|
||||
../../include/dcb.h ../../include/spinlock.h ../../include/thread.h \
|
||||
/usr/include/pthread.h /usr/include/sched.h /usr/include/bits/sched.h \
|
||||
/usr/include/bits/setjmp.h ../../include/buffer.h \
|
||||
../../include/gwbitmask.h ../../include/service.h ../../include/server.h \
|
||||
../../include/session.h /usr/include/sys/ioctl.h \
|
||||
/usr/include/bits/ioctls.h /usr/include/asm/ioctls.h \
|
||||
/usr/include/asm-generic/ioctls.h /usr/include/linux/ioctl.h \
|
||||
/usr/include/asm/ioctl.h /usr/include/asm-generic/ioctl.h \
|
||||
/usr/include/bits/ioctl-types.h /usr/include/sys/ttydefaults.h \
|
||||
/usr/include/errno.h /usr/include/bits/errno.h \
|
||||
/usr/include/linux/errno.h /usr/include/asm/errno.h \
|
||||
/usr/include/asm-generic/errno.h /usr/include/asm-generic/errno-base.h \
|
||||
/usr/include/sys/socket.h /usr/include/sys/uio.h /usr/include/bits/uio.h \
|
||||
/usr/include/bits/socket.h /usr/include/bits/sockaddr.h \
|
||||
/usr/include/asm/socket.h /usr/include/asm-generic/socket.h \
|
||||
/usr/include/asm/sockios.h /usr/include/asm-generic/sockios.h \
|
||||
/usr/include/netinet/in.h /usr/include/stdint.h \
|
||||
/usr/include/bits/wchar.h /usr/include/bits/in.h \
|
||||
/usr/include/arpa/inet.h ../../include/router.h ../../include/poll.h \
|
||||
../../include/atomic.h ../../include/gw.h /usr/include/ctype.h \
|
||||
/usr/include/netdb.h /usr/include/rpc/netdb.h /usr/include/bits/netdb.h \
|
||||
/usr/include/fcntl.h /usr/include/bits/fcntl.h /usr/include/bits/stat.h \
|
||||
/usr/include/syslog.h /usr/include/sys/syslog.h \
|
||||
/usr/include/bits/syslog-path.h /usr/include/pwd.h \
|
||||
/usr/include/sys/epoll.h /usr/include/signal.h \
|
||||
/usr/include/bits/signum.h /usr/include/bits/siginfo.h \
|
||||
/usr/include/bits/sigaction.h /usr/include/bits/sigcontext.h \
|
||||
/usr/include/bits/sigstack.h /usr/include/sys/ucontext.h \
|
||||
/usr/include/bits/sigthread.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdbool.h \
|
||||
../../include/gateway_mysql.h ../../include/mysql_protocol.h \
|
||||
../../include/dcb.h ../include/telnetd.h ../../include/adminusers.h
|
||||
httpd.o: httpd.c ../include/httpd.h /usr/include/stdio.h \
|
||||
/usr/include/features.h /usr/include/sys/cdefs.h \
|
||||
/usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
|
||||
/usr/include/gnu/stubs-64.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stddef.h \
|
||||
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
|
||||
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdarg.h \
|
||||
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
|
||||
/usr/include/stdlib.h /usr/include/bits/waitflags.h \
|
||||
/usr/include/bits/waitstatus.h /usr/include/endian.h \
|
||||
/usr/include/bits/endian.h /usr/include/bits/byteswap.h \
|
||||
/usr/include/sys/types.h /usr/include/time.h /usr/include/sys/select.h \
|
||||
/usr/include/bits/select.h /usr/include/bits/sigset.h \
|
||||
/usr/include/bits/time.h /usr/include/sys/sysmacros.h \
|
||||
/usr/include/bits/pthreadtypes.h /usr/include/alloca.h \
|
||||
/usr/include/string.h /usr/include/xlocale.h ../../include/dcb.h \
|
||||
../../include/spinlock.h ../../include/thread.h /usr/include/pthread.h \
|
||||
/usr/include/sched.h /usr/include/bits/sched.h \
|
||||
/usr/include/bits/setjmp.h ../../include/buffer.h \
|
||||
../../include/gwbitmask.h ../../include/service.h ../../include/server.h \
|
||||
../../include/session.h /usr/include/sys/ioctl.h \
|
||||
/usr/include/bits/ioctls.h /usr/include/asm/ioctls.h \
|
||||
/usr/include/asm-generic/ioctls.h /usr/include/linux/ioctl.h \
|
||||
/usr/include/asm/ioctl.h /usr/include/asm-generic/ioctl.h \
|
||||
/usr/include/bits/ioctl-types.h /usr/include/sys/ttydefaults.h \
|
||||
/usr/include/errno.h /usr/include/bits/errno.h \
|
||||
/usr/include/linux/errno.h /usr/include/asm/errno.h \
|
||||
/usr/include/asm-generic/errno.h /usr/include/asm-generic/errno-base.h \
|
||||
/usr/include/sys/socket.h /usr/include/sys/uio.h /usr/include/bits/uio.h \
|
||||
/usr/include/bits/socket.h /usr/include/bits/sockaddr.h \
|
||||
/usr/include/asm/socket.h /usr/include/asm-generic/socket.h \
|
||||
/usr/include/asm/sockios.h /usr/include/asm-generic/sockios.h \
|
||||
/usr/include/netinet/in.h /usr/include/stdint.h \
|
||||
/usr/include/bits/wchar.h /usr/include/bits/in.h \
|
||||
/usr/include/arpa/inet.h ../../include/router.h ../../include/poll.h \
|
||||
../../include/atomic.h ../../include/gw.h /usr/include/ctype.h \
|
||||
/usr/include/netdb.h /usr/include/rpc/netdb.h /usr/include/bits/netdb.h \
|
||||
/usr/include/fcntl.h /usr/include/bits/fcntl.h /usr/include/bits/stat.h \
|
||||
/usr/include/unistd.h /usr/include/bits/posix_opt.h \
|
||||
/usr/include/bits/environments.h /usr/include/bits/confname.h \
|
||||
/usr/include/getopt.h /usr/include/syslog.h /usr/include/sys/syslog.h \
|
||||
/usr/include/bits/syslog-path.h /usr/include/pwd.h \
|
||||
/usr/include/sys/epoll.h /usr/include/signal.h \
|
||||
/usr/include/bits/signum.h /usr/include/bits/siginfo.h \
|
||||
/usr/include/bits/sigaction.h /usr/include/bits/sigcontext.h \
|
||||
/usr/include/bits/sigstack.h /usr/include/sys/ucontext.h \
|
||||
/usr/include/bits/sigthread.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdbool.h \
|
||||
../../include/gateway_mysql.h ../../include/mysql_protocol.h \
|
||||
../../include/dcb.h
|
||||
466
server/modules/protocol/httpd.c
Normal file
466
server/modules/protocol/httpd.c
Normal file
@ -0,0 +1,466 @@
|
||||
/*
|
||||
* This file is distributed as part of the SkySQL Gateway. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright SkySQL Ab 2013
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file httpd.c - HTTP daemon protocol module
|
||||
*
|
||||
* The httpd protocol module is intended as a mechanism to allow connections
|
||||
* into the gateway for the purpose of accessing information within
|
||||
* the gateway with a REST interface
|
||||
* databases.
|
||||
*
|
||||
* In the first instance it is intended to allow a debug connection to access
|
||||
* internal data structures, however it may also be used to manage the
|
||||
* configuration of the gateway via REST interface.
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
* Date Who Description
|
||||
* 08/07/2013 Massimiliano Pinto Initial version
|
||||
* 09/07/2013 Massimiliano Pinto Added /show?dcb|session for all dcbs|sessions
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
#include <httpd.h>
|
||||
|
||||
#define ISspace(x) isspace((int)(x))
|
||||
#define HTTP_SERVER_STRING "Gateway(c) v.1.0.0"
|
||||
static char *version_str = "V1.0.1";
|
||||
|
||||
static int httpd_read_event(DCB* dcb);
|
||||
static int httpd_write_event(DCB *dcb);
|
||||
static int httpd_write(DCB *dcb, GWBUF *queue);
|
||||
static int httpd_error(DCB *dcb);
|
||||
static int httpd_hangup(DCB *dcb);
|
||||
static int httpd_accept(DCB *dcb);
|
||||
static int httpd_close(DCB *dcb);
|
||||
static int httpd_listen(DCB *dcb, char *config);
|
||||
static int httpd_get_line(int sock, char *buf, int size);
|
||||
static void httpd_send_headers(DCB *dcb, int final);
|
||||
|
||||
/**
|
||||
* The "module object" for the httpd protocol module.
|
||||
*/
|
||||
static GWPROTOCOL MyObject = {
|
||||
httpd_read_event, /**< Read - EPOLLIN handler */
|
||||
httpd_write, /**< Write - data from gateway */
|
||||
httpd_write_event, /**< WriteReady - EPOLLOUT handler */
|
||||
httpd_error, /**< Error - EPOLLERR handler */
|
||||
httpd_hangup, /**< HangUp - EPOLLHUP handler */
|
||||
httpd_accept, /**< Accept */
|
||||
NULL, /**< Connect */
|
||||
httpd_close, /**< Close */
|
||||
httpd_listen, /**< Create a listener */
|
||||
NULL, /**< Authentication */
|
||||
NULL /**< Session */
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of the mandatory version entry point
|
||||
*
|
||||
* @return version string of the module
|
||||
*/
|
||||
char *
|
||||
version()
|
||||
{
|
||||
return version_str;
|
||||
}
|
||||
|
||||
/**
|
||||
* The module initialisation routine, called when the module
|
||||
* is first loaded.
|
||||
*/
|
||||
void
|
||||
ModuleInit()
|
||||
{
|
||||
fprintf(stderr, "Initialise HTTPD Protocol module.\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* The module entry point routine. It is this routine that
|
||||
* must populate the structure that is referred to as the
|
||||
* "module object", this is a structure with the set of
|
||||
* external entry points for this module.
|
||||
*
|
||||
* @return The module object
|
||||
*/
|
||||
GWPROTOCOL *
|
||||
GetModuleObject()
|
||||
{
|
||||
return &MyObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read event for EPOLLIN on the httpd protocol module.
|
||||
*
|
||||
* @param dcb The descriptor control block
|
||||
* @return
|
||||
*/
|
||||
static int
|
||||
httpd_read_event(DCB* dcb)
|
||||
{
|
||||
//SESSION *session = dcb->session;
|
||||
//ROUTER_OBJECT *router = session->service->router;
|
||||
//ROUTER *router_instance = session->service->router_instance;
|
||||
//void *rsession = session->router_session;
|
||||
|
||||
int numchars = 1;
|
||||
char buf[HTTPD_REQUESTLINE_MAXLEN-1] = "";
|
||||
char *query_string = NULL;
|
||||
char method[HTTPD_METHOD_MAXLEN-1] = "";
|
||||
char url[HTTPD_SMALL_BUFFER] = "";
|
||||
int cgi = 0;
|
||||
size_t i, j;
|
||||
int headers_read = 0;
|
||||
HTTPD_session *client_data = NULL;
|
||||
|
||||
dcb->state = DCB_STATE_PROCESSING;
|
||||
|
||||
client_data = dcb->data;
|
||||
|
||||
/**
|
||||
* get the request line
|
||||
* METHOD URL HTTP_VER\r\n
|
||||
*/
|
||||
|
||||
numchars = httpd_get_line(dcb->fd, buf, sizeof(buf));
|
||||
|
||||
i = 0; j = 0;
|
||||
while (!ISspace(buf[j]) && (i < sizeof(method) - 1)) {
|
||||
method[i] = buf[j];
|
||||
i++; j++;
|
||||
}
|
||||
method[i] = '\0';
|
||||
|
||||
strcpy(client_data->method, method);
|
||||
|
||||
/* check allowed http methods */
|
||||
if (strcasecmp(method, "GET") && strcasecmp(method, "POST")) {
|
||||
//httpd_unimplemented(dcb->fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcasecmp(method, "POST") == 0)
|
||||
cgi = 1;
|
||||
|
||||
i = 0;
|
||||
|
||||
while (ISspace(buf[j]) && (j < sizeof(buf))) {
|
||||
j++;
|
||||
}
|
||||
|
||||
while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf))) {
|
||||
url[i] = buf[j];
|
||||
i++; j++;
|
||||
}
|
||||
|
||||
url[i] = '\0';
|
||||
|
||||
/**
|
||||
* Get the query string if availble
|
||||
*/
|
||||
|
||||
if (strcasecmp(method, "GET") == 0) {
|
||||
query_string = url;
|
||||
while ((*query_string != '?') && (*query_string != '\0'))
|
||||
query_string++;
|
||||
if (*query_string == '?') {
|
||||
cgi = 1;
|
||||
*query_string = '\0';
|
||||
query_string++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the request headers
|
||||
*/
|
||||
|
||||
while ((numchars > 0) && strcmp("\n", buf)) {
|
||||
char *value = NULL;
|
||||
char *end = NULL;
|
||||
numchars = httpd_get_line(dcb->fd, buf, sizeof(buf));
|
||||
if ( (value = strchr(buf, ':'))) {
|
||||
*value = '\0';
|
||||
value++;
|
||||
end = &value[strlen(value) -1];
|
||||
*end = '\0';
|
||||
|
||||
if (strncasecmp(buf, "Hostname", 6) == 0) {
|
||||
strcpy(client_data->hostname, value);
|
||||
}
|
||||
if (strncasecmp(buf, "useragent", 9) == 0) {
|
||||
strcpy(client_data->useragent, value);
|
||||
}
|
||||
//fprintf(stderr, "<<< Header [%s:%s]\n", buf, value);
|
||||
}
|
||||
}
|
||||
|
||||
if (numchars) {
|
||||
headers_read = 1;
|
||||
memcpy(&client_data->headers_received, &headers_read, sizeof(int));
|
||||
}
|
||||
|
||||
/**
|
||||
* Now begins the server reply
|
||||
*/
|
||||
|
||||
/* send all the basic headers and close with \r\n */
|
||||
httpd_send_headers(dcb, 1);
|
||||
|
||||
/**
|
||||
* ToDO: launch proper content handling based on the requested URI, later REST interface
|
||||
*
|
||||
*/
|
||||
|
||||
dcb_printf(dcb, "Welcome to HTTPD Gateway (c) %s\n\n", version_str);
|
||||
|
||||
if (strcmp(url, "/show") == 0) {
|
||||
if (strlen(query_string)) {
|
||||
if (strcmp(query_string, "dcb") == 0)
|
||||
dprintAllDCBs(dcb);
|
||||
if (strcmp(query_string, "session") == 0)
|
||||
dprintAllSessions(dcb);
|
||||
}
|
||||
}
|
||||
|
||||
/* force the client connecton close */
|
||||
dcb->func.close(dcb);
|
||||
|
||||
dcb->state = DCB_STATE_POLLING;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* EPOLLOUT handler for the HTTPD protocol module.
|
||||
*
|
||||
* @param dcb The descriptor control block
|
||||
* @return
|
||||
*/
|
||||
static int
|
||||
httpd_write_event(DCB *dcb)
|
||||
{
|
||||
return dcb_drain_writeq(dcb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write routine for the HTTPD protocol module.
|
||||
*
|
||||
* Writes the content of the buffer queue to the socket
|
||||
* observing the non-blocking principles of the gateway.
|
||||
*
|
||||
* @param dcb Descriptor Control Block for the socket
|
||||
* @param queue Linked list of buffes to write
|
||||
*/
|
||||
static int
|
||||
httpd_write(DCB *dcb, GWBUF *queue)
|
||||
{
|
||||
return dcb_write(dcb, queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the EPOLLERR event.
|
||||
*
|
||||
* @param dcb The descriptor control block
|
||||
*/
|
||||
static int
|
||||
httpd_error(DCB *dcb)
|
||||
{
|
||||
dcb_close(dcb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the EPOLLHUP event.
|
||||
*
|
||||
* @param dcb The descriptor control block
|
||||
*/
|
||||
static int
|
||||
httpd_hangup(DCB *dcb)
|
||||
{
|
||||
dcb_close(dcb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the EPOLLIN event when the DCB refers to the listening
|
||||
* socket for the protocol.
|
||||
*
|
||||
* @param dcb The descriptor control block
|
||||
*/
|
||||
static int
|
||||
httpd_accept(DCB *dcb)
|
||||
{
|
||||
int n_connect = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
int so = -1;
|
||||
struct sockaddr_in addr;
|
||||
socklen_t addrlen;
|
||||
DCB *client = NULL;
|
||||
HTTPD_session *client_data = NULL;
|
||||
|
||||
if ((so = accept(dcb->fd, (struct sockaddr *)&addr, &addrlen)) == -1)
|
||||
return n_connect;
|
||||
else
|
||||
{
|
||||
atomic_add(&dcb->stats.n_accepts, 1);
|
||||
client = dcb_alloc();
|
||||
client->fd = so;
|
||||
client->remote = strdup(inet_ntoa(addr.sin_addr));
|
||||
memcpy(&client->func, &MyObject, sizeof(GWPROTOCOL));
|
||||
client->session = session_alloc(dcb->session->service, client);
|
||||
|
||||
client->state = DCB_STATE_IDLE;
|
||||
|
||||
if (poll_add_dcb(client) == -1)
|
||||
{
|
||||
return n_connect;
|
||||
}
|
||||
n_connect++;
|
||||
|
||||
/* create the session data for HTTPD */
|
||||
client_data = (HTTPD_session *)calloc(1, sizeof(HTTPD_session));
|
||||
client->data = client_data;
|
||||
|
||||
client->state = DCB_STATE_POLLING;
|
||||
}
|
||||
}
|
||||
return n_connect;
|
||||
}
|
||||
|
||||
/**
|
||||
* The close handler for the descriptor. Called by the gateway to
|
||||
* explicitly close a connection.
|
||||
*
|
||||
* @param dcb The descriptor control block
|
||||
*/
|
||||
|
||||
static int
|
||||
httpd_close(DCB *dcb)
|
||||
{
|
||||
dcb_close(dcb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTTP daemon listener entry point
|
||||
*
|
||||
* @param listener The Listener DCB
|
||||
* @param config Configuration (ip:port)
|
||||
*/
|
||||
static int
|
||||
httpd_listen(DCB *listener, char *config)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
char *port;
|
||||
int one = 1;
|
||||
short pnum;
|
||||
|
||||
memcpy(&listener->func, &MyObject, sizeof(GWPROTOCOL));
|
||||
|
||||
port = strrchr(config, ':');
|
||||
if (port)
|
||||
port++;
|
||||
else
|
||||
port = "6442";
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
pnum = atoi(port);
|
||||
addr.sin_port = htons(pnum);
|
||||
|
||||
if ((listener->fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* socket options */
|
||||
setsockopt(listener->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one));
|
||||
|
||||
/* set NONBLOCKING mode */
|
||||
setnonblocking(listener->fd);
|
||||
|
||||
/* bind address and port */
|
||||
if (bind(listener->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
listener->state = DCB_STATE_LISTENING;
|
||||
listen(listener->fd, SOMAXCONN);
|
||||
|
||||
if (poll_add_dcb(listener) == -1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTPD get line from client
|
||||
*/
|
||||
static int httpd_get_line(int sock, char *buf, int size) {
|
||||
int i = 0;
|
||||
char c = '\0';
|
||||
int n;
|
||||
|
||||
while ((i < size - 1) && (c != '\n')) {
|
||||
n = recv(sock, &c, 1, 0);
|
||||
/* DEBUG printf("%02X\n", c); */
|
||||
if (n > 0) {
|
||||
if (c == '\r') {
|
||||
n = recv(sock, &c, 1, MSG_PEEK);
|
||||
/* DEBUG printf("%02X\n", c); */
|
||||
if ((n > 0) && (c == '\n'))
|
||||
recv(sock, &c, 1, 0);
|
||||
else
|
||||
c = '\n';
|
||||
}
|
||||
buf[i] = c;
|
||||
i++;
|
||||
} else
|
||||
c = '\n';
|
||||
}
|
||||
|
||||
buf[i] = '\0';
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTPD send basic headers with 200 OK
|
||||
*/
|
||||
static void httpd_send_headers(DCB *dcb, int final)
|
||||
{
|
||||
char date[64] = "";
|
||||
const char *fmt = "%a, %d %b %Y %H:%M:%S GMT";
|
||||
time_t httpd_current_time = time(NULL);
|
||||
|
||||
strftime(date, sizeof(date), fmt, localtime(&httpd_current_time));
|
||||
|
||||
dcb_printf(dcb, "HTTP/1.1 200 OK\r\nDate: %s\r\nServer: %s\r\nConnection: close\r\nContent-Type: text/plain\r\n", date, HTTP_SERVER_STRING);
|
||||
|
||||
/* close the headers */
|
||||
if (final) {
|
||||
dcb_printf(dcb, "\r\n");
|
||||
}
|
||||
}
|
||||
//
|
||||
561
server/modules/protocol/mysql_backend.c
Normal file
561
server/modules/protocol/mysql_backend.c
Normal file
@ -0,0 +1,561 @@
|
||||
/*
|
||||
* This file is distributed as part of the SkySQL Gateway. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright SkySQL Ab 2013
|
||||
*/
|
||||
|
||||
#include "mysql_client_server_protocol.h"
|
||||
#if defined(SS_DEBUG)
|
||||
#include <skygw_types.h>
|
||||
#include <skygw_utils.h>
|
||||
#include <log_manager.h>
|
||||
#endif
|
||||
/*
|
||||
* MySQL Protocol module for handling the protocol between the gateway
|
||||
* and the backend MySQL database.
|
||||
*
|
||||
* Revision History
|
||||
* Date Who Description
|
||||
* 14/06/2013 Mark Riddoch Initial version
|
||||
* 17/06/2013 Massimiliano Pinto Added Gateway To Backends routines
|
||||
* 27/06/2013 Vilho Raatikka Added skygw_log_write command as an example
|
||||
* and necessary headers.
|
||||
* 01/07/2013 Massimiliano Pinto Put Log Manager example code behind SS_DEBUG macros.
|
||||
* 03/07/2013 Massimiliano Pinto Added delayq for incoming data before mysql connection
|
||||
* 04/07/2013 Massimiliano Pinto Added asyncrhronous MySQL protocol connection to backend
|
||||
* 05/07/2013 Massimiliano Pinto Added closeSession if backend auth fails
|
||||
* 12/07/2013 Massimiliano Pinto Added Mysql Change User via dcb->func.auth()
|
||||
* 15/07/2013 Massimiliano Pinto Added Mysql session change via dcb->func.session()
|
||||
* 17/07/2013 Massimiliano Pinto Added dcb->command update from gwbuf->command for proper routing
|
||||
server replies to client via router->clientReply
|
||||
*/
|
||||
|
||||
static char *version_str = "V2.0.0";
|
||||
int gw_mysql_connect(char *host, int port, char *dbname, char *user, uint8_t *passwd, MySQLProtocol *conn);
|
||||
static int gw_create_backend_connection(DCB *backend, SERVER *server, SESSION *in_session);
|
||||
static int gw_read_backend_event(DCB* dcb);
|
||||
static int gw_write_backend_event(DCB *dcb);
|
||||
static int gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue);
|
||||
static int gw_error_backend_event(DCB *dcb);
|
||||
static int gw_backend_close(DCB *dcb);
|
||||
static int gw_backend_hangup(DCB *dcb);
|
||||
static int backend_write_delayqueue(DCB *dcb);
|
||||
static void backend_set_delayqueue(DCB *dcb, GWBUF *queue);
|
||||
static int gw_change_user(DCB *backend_dcb, SERVER *server, SESSION *in_session, GWBUF *queue);
|
||||
static int gw_session(DCB *backend_dcb, void *data);
|
||||
|
||||
extern char *gw_strend(register const char *s);
|
||||
|
||||
static GWPROTOCOL MyObject = {
|
||||
gw_read_backend_event, /* Read - EPOLLIN handler */
|
||||
gw_MySQLWrite_backend, /* Write - data from gateway */
|
||||
gw_write_backend_event, /* WriteReady - EPOLLOUT handler */
|
||||
gw_error_backend_event, /* Error - EPOLLERR handler */
|
||||
gw_backend_hangup, /* HangUp - EPOLLHUP handler */
|
||||
NULL, /* Accept */
|
||||
gw_create_backend_connection, /* Connect */
|
||||
gw_backend_close, /* Close */
|
||||
NULL, /* Listen */
|
||||
gw_change_user, /* Authentication */
|
||||
gw_session /* Session */
|
||||
};
|
||||
|
||||
/*
|
||||
* Implementation of the mandatory version entry point
|
||||
*
|
||||
* @return version string of the module
|
||||
*/
|
||||
char *
|
||||
version()
|
||||
{
|
||||
return version_str;
|
||||
}
|
||||
|
||||
/*
|
||||
* The module initialisation routine, called when the module
|
||||
* is first loaded.
|
||||
*/
|
||||
void
|
||||
ModuleInit()
|
||||
{
|
||||
#if defined(SS_DEBUG)
|
||||
skygw_log_write(NULL,
|
||||
LOGFILE_MESSAGE,
|
||||
strdup("Initial MySQL Backend Protcol module."));
|
||||
#endif
|
||||
fprintf(stderr, "Initial MySQL Backend Protcol module.\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* The module entry point routine. It is this routine that
|
||||
* must populate the structure that is referred to as the
|
||||
* "module object", this is a structure with the set of
|
||||
* external entry points for this module.
|
||||
*
|
||||
* @return The module object
|
||||
*/
|
||||
GWPROTOCOL *
|
||||
GetModuleObject()
|
||||
{
|
||||
return &MyObject;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Backend Read Event for EPOLLIN on the MySQL backend protocol module
|
||||
* @param dcb The backend Descriptor Control Block
|
||||
* @return 1 on operation, 0 for no action
|
||||
*/
|
||||
static int gw_read_backend_event(DCB *dcb) {
|
||||
MySQLProtocol *client_protocol = NULL;
|
||||
MySQLProtocol *backend_protocol = NULL;
|
||||
MYSQL_session *current_session = NULL;
|
||||
|
||||
if(dcb->session) {
|
||||
client_protocol = SESSION_PROTOCOL(dcb->session, MySQLProtocol);
|
||||
}
|
||||
|
||||
backend_protocol = (MySQLProtocol *) dcb->protocol;
|
||||
current_session = (MYSQL_session *)dcb->session->data;
|
||||
|
||||
//fprintf(stderr, ">>> backend EPOLLIN from %i, command %i, protocol state [%s]\n", dcb->fd, dcb->command, gw_mysql_protocol_state2string(backend_protocol->state));
|
||||
|
||||
/* backend is connected:
|
||||
*
|
||||
* 1. read server handshake
|
||||
* 2. and write auth request
|
||||
* 3. and return
|
||||
*/
|
||||
if (backend_protocol->state == MYSQL_CONNECTED) {
|
||||
|
||||
gw_read_backend_handshake(backend_protocol);
|
||||
|
||||
gw_send_authentication_to_backend(current_session->db, current_session->user, current_session->client_sha1, backend_protocol);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ready to check the authentication reply from backend */
|
||||
|
||||
if (backend_protocol->state == MYSQL_AUTH_RECV) {
|
||||
ROUTER_OBJECT *router = NULL;
|
||||
ROUTER *router_instance = NULL;
|
||||
void *rsession = NULL;
|
||||
int rv = -1;
|
||||
SESSION *session = dcb->session;
|
||||
|
||||
if (session) {
|
||||
router = session->service->router;
|
||||
router_instance = session->service->router_instance;
|
||||
rsession = session->router_session;
|
||||
}
|
||||
|
||||
/* read backed auth reply */
|
||||
rv = gw_receive_backend_auth(backend_protocol);
|
||||
|
||||
switch (rv) {
|
||||
case MYSQL_FAILED_AUTHENTICATION:
|
||||
fprintf(stderr, ">>>> Backend Auth failed for user [%s], fd %i\n", current_session->user, dcb->fd);
|
||||
|
||||
backend_protocol->state = MYSQL_AUTH_FAILED;
|
||||
|
||||
/* send an error to the client */
|
||||
mysql_send_custom_error(dcb->session->client, 1, 0, "Connection to backend lost right now");
|
||||
|
||||
/* close the active session */
|
||||
router->closeSession(router_instance, rsession);
|
||||
|
||||
/* force the router_session to NULL
|
||||
* Later we will implement a proper status for the session
|
||||
*/
|
||||
session->router_session = NULL;
|
||||
|
||||
return 1;
|
||||
|
||||
case MYSQL_SUCCESFUL_AUTHENTICATION:
|
||||
spinlock_acquire(&dcb->authlock);
|
||||
|
||||
backend_protocol->state = MYSQL_IDLE;
|
||||
|
||||
/* check the delay queue and flush the data */
|
||||
if(dcb->delayq) {
|
||||
backend_write_delayqueue(dcb);
|
||||
spinlock_release(&dcb->authlock);
|
||||
return 1;
|
||||
}
|
||||
spinlock_release(&dcb->authlock);
|
||||
|
||||
return 1;
|
||||
|
||||
default:
|
||||
/* no other authentication state here right now, so just return */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* reading MySQL command output from backend and writing to the client */
|
||||
|
||||
if ((client_protocol->state == MYSQL_WAITING_RESULT) || (client_protocol->state == MYSQL_IDLE)) {
|
||||
GWBUF *head = NULL;
|
||||
ROUTER_OBJECT *router = NULL;
|
||||
ROUTER *router_instance = NULL;
|
||||
void *rsession = NULL;
|
||||
SESSION *session = dcb->session;
|
||||
|
||||
/* read available backend data */
|
||||
dcb_read(dcb, &head);
|
||||
|
||||
if (session) {
|
||||
router = session->service->router;
|
||||
router_instance = session->service->router_instance;
|
||||
rsession = session->router_session;
|
||||
}
|
||||
|
||||
/* Note the gwbuf doesn't have here a valid queue->command descriptions as it is a fresh new one!
|
||||
* We only have the copied value in dcb->command from previuos func.write()
|
||||
* and this will be used by the router->clientReply
|
||||
*/
|
||||
|
||||
/* and pass now the gwbuf to the router */
|
||||
router->clientReply(router_instance, rsession, head, dcb);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* EPOLLOUT handler for the MySQL Backend protocol module.
|
||||
*
|
||||
* @param dcb The descriptor control block
|
||||
* @return The number of bytes written
|
||||
*/
|
||||
static int gw_write_backend_event(DCB *dcb) {
|
||||
MySQLProtocol *backend_protocol = dcb->protocol;
|
||||
|
||||
//fprintf(stderr, ">>> backend EPOLLOUT %i, protocol state [%s]\n", backend_protocol->fd, gw_mysql_protocol_state2string(backend_protocol->state));
|
||||
|
||||
// spinlock_acquire(&dcb->connectlock);
|
||||
|
||||
if (backend_protocol->state == MYSQL_PENDING_CONNECT) {
|
||||
backend_protocol->state = MYSQL_CONNECTED;
|
||||
|
||||
// spinlock_release(&dcb->connectlock);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// spinlock_release(&dcb->connectlock);
|
||||
|
||||
return dcb_drain_writeq(dcb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write function for backend DCB
|
||||
*
|
||||
* @param dcb The DCB of the backend
|
||||
* @param queue Queue of buffers to write
|
||||
* @return 0 on failure, 1 on success
|
||||
*/
|
||||
static int
|
||||
gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue)
|
||||
{
|
||||
MySQLProtocol *backend_protocol = dcb->protocol;
|
||||
|
||||
spinlock_acquire(&dcb->authlock);
|
||||
|
||||
/**
|
||||
* Now put the incoming data to the delay queue unless backend is connected with auth ok
|
||||
*/
|
||||
if (backend_protocol->state != MYSQL_IDLE) {
|
||||
//fprintf(stderr, ">>> Writing in the backend %i delay queue: last dcb command %i, queue command %i, protocol state [%s]\n", dcb->fd, dcb->command, queue->command, gw_mysql_protocol_state2string(dcb->state));
|
||||
|
||||
backend_set_delayqueue(dcb, queue);
|
||||
spinlock_release(&dcb->authlock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Now we set the last command received, from the current queue
|
||||
*/
|
||||
memcpy(&dcb->command, &queue->command, sizeof(dcb->command));
|
||||
|
||||
spinlock_release(&dcb->authlock);
|
||||
|
||||
return dcb_write(dcb, queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Backend Error Handling
|
||||
*
|
||||
*/
|
||||
static int gw_error_backend_event(DCB *dcb) {
|
||||
|
||||
fprintf(stderr, ">>> Handle Backend error function for %i\n", dcb->fd);
|
||||
|
||||
dcb_close(dcb);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new backend connection.
|
||||
*
|
||||
* This routine will connect to a backend server and it is called by dbc_connect in router->newSession
|
||||
*
|
||||
* @param backend The Backend DCB allocated from dcb_connect
|
||||
* @param server The selected server to connect to
|
||||
* @param session The current session from Client DCB
|
||||
* @return 0 on Success or 1 on Failure.
|
||||
*/
|
||||
|
||||
static int gw_create_backend_connection(DCB *backend, SERVER *server, SESSION *session) {
|
||||
MySQLProtocol *protocol = NULL;
|
||||
MYSQL_session *s_data = NULL;
|
||||
int rv = -1;
|
||||
|
||||
protocol = (MySQLProtocol *) calloc(1, sizeof(MySQLProtocol));
|
||||
protocol->state = MYSQL_ALLOC;
|
||||
|
||||
backend->protocol = protocol;
|
||||
|
||||
/* put the backend dcb in the protocol struct */
|
||||
protocol->descriptor = backend;
|
||||
|
||||
s_data = (MYSQL_session *)session->client->data;
|
||||
|
||||
/**
|
||||
* let's try to connect to a backend server, only connect sys call
|
||||
* The socket descriptor is in Non Blocking status, this is set in the function
|
||||
*/
|
||||
rv = gw_do_connect_to_backend(server->name, server->port, protocol);
|
||||
|
||||
// we could also move later, this in to the gw_do_connect_to_backend using protocol->descriptor
|
||||
|
||||
memcpy(&backend->fd, &protocol->fd, sizeof(backend->fd));
|
||||
|
||||
switch (rv) {
|
||||
|
||||
case 0:
|
||||
//fprintf(stderr, "Connected to backend mysql server: fd is %i\n", backend->fd);
|
||||
protocol->state = MYSQL_CONNECTED;
|
||||
|
||||
break;
|
||||
|
||||
case 1:
|
||||
//fprintf(stderr, ">>> Connection is PENDING to backend mysql server: fd is %i\n", backend->fd);
|
||||
protocol->state = MYSQL_PENDING_CONNECT;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, ">>> ERROR: NOT Connected to the backend mysql server!!!\n");
|
||||
backend->fd = -1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
fprintf(stderr, ">>> Backend [%s:%i] added [%i], in the client session [%i]\n", server->name, server->port, backend->fd, session->client->fd);
|
||||
|
||||
backend->state = DCB_STATE_POLLING;
|
||||
|
||||
return backend->fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hangup routine the backend dcb: it does nothing right now
|
||||
*
|
||||
* @param dcb The current Backend DCB
|
||||
* @return 1 always
|
||||
*/
|
||||
static int
|
||||
gw_backend_hangup(DCB *dcb)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the backend dcb
|
||||
*
|
||||
* @param dcb The current Backend DCB
|
||||
* @return 1 always
|
||||
*/
|
||||
static int
|
||||
gw_backend_close(DCB *dcb)
|
||||
{
|
||||
dcb_close(dcb);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* This routine put into the delay queue the input queue
|
||||
* The input is what backend DCB is receiving
|
||||
* The routine is called from func.write() when mysql backend connection
|
||||
* is not yet complete buu there are inout data from client
|
||||
*
|
||||
* @param dcb The current backend DCB
|
||||
* @param queue Input data in the GWBUF struct
|
||||
*/
|
||||
static void backend_set_delayqueue(DCB *dcb, GWBUF *queue) {
|
||||
spinlock_acquire(&dcb->delayqlock);
|
||||
|
||||
if (dcb->delayq) {
|
||||
/* Append data */
|
||||
dcb->delayq = gwbuf_append(dcb->delayq, queue);
|
||||
} else {
|
||||
if (queue != NULL) {
|
||||
/* create the delay queue */
|
||||
dcb->delayq = queue;
|
||||
}
|
||||
}
|
||||
|
||||
spinlock_release(&dcb->delayqlock);
|
||||
}
|
||||
|
||||
/**
|
||||
* This routine writes the delayq via dcb_write
|
||||
* The dcb->delayq contains data received from the client before
|
||||
* mysql backend authentication succeded
|
||||
*
|
||||
* @param dcb The current backend DCB
|
||||
* @return The dcb_write status
|
||||
*/
|
||||
static int backend_write_delayqueue(DCB *dcb)
|
||||
{
|
||||
GWBUF *localq = NULL;
|
||||
|
||||
spinlock_acquire(&dcb->delayqlock);
|
||||
|
||||
localq = dcb->delayq;
|
||||
dcb->delayq = NULL;
|
||||
|
||||
/**
|
||||
* Now we set the last command received, from the delayed queue
|
||||
*/
|
||||
|
||||
memcpy(&dcb->command, &localq->command, sizeof(dcb->command));
|
||||
|
||||
spinlock_release(&dcb->delayqlock);
|
||||
|
||||
return dcb_write(dcb, localq);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int gw_change_user(DCB *backend, SERVER *server, SESSION *in_session, GWBUF *queue) {
|
||||
MYSQL_session *current_session = NULL;
|
||||
MySQLProtocol *backend_protocol = NULL;
|
||||
MySQLProtocol *client_protocol = NULL;
|
||||
char username[MYSQL_USER_MAXLEN+1]="";
|
||||
char database[MYSQL_DATABASE_MAXLEN+1]="";
|
||||
uint8_t client_sha1[MYSQL_SCRAMBLE_LEN]="";
|
||||
uint8_t *client_auth_packet = GWBUF_DATA(queue);
|
||||
unsigned int auth_token_len = 0;
|
||||
uint8_t *auth_token = NULL;
|
||||
int rv = -1;
|
||||
int len = 0;
|
||||
int auth_ret = 1;
|
||||
|
||||
current_session = (MYSQL_session *)in_session->client->data;
|
||||
backend_protocol = backend->protocol;
|
||||
client_protocol = in_session->client->protocol;
|
||||
|
||||
queue->command = ROUTER_CHANGE_SESSION;
|
||||
|
||||
// now get the user, after 4 bytes header and 1 byte command
|
||||
client_auth_packet += 5;
|
||||
strcpy(username, (char *)client_auth_packet);
|
||||
client_auth_packet += strlen(username) + 1;
|
||||
|
||||
// get the auth token len
|
||||
memcpy(&auth_token_len, client_auth_packet, 1);
|
||||
client_auth_packet++;
|
||||
|
||||
// allocate memory for token only if auth_token_len > 0
|
||||
if (auth_token_len) {
|
||||
auth_token = (uint8_t *)malloc(auth_token_len);
|
||||
memcpy(auth_token, client_auth_packet, auth_token_len);
|
||||
client_auth_packet += auth_token_len;
|
||||
}
|
||||
|
||||
// decode the token and check the password
|
||||
// Note: if auth_token_len == 0 && auth_token == NULL, user is without password
|
||||
auth_ret = gw_check_mysql_scramble_data(backend->session->client, auth_token, auth_token_len, client_protocol->scramble, sizeof(client_protocol->scramble), username, client_sha1);
|
||||
|
||||
// let's free the auth_token now
|
||||
if (auth_token)
|
||||
free(auth_token);
|
||||
|
||||
if (auth_ret != 0) {
|
||||
fprintf(stderr, "<<< CLIENT AUTH FAILED for user [%s], user session will not change!\n", username);
|
||||
|
||||
// send the error packet
|
||||
mysql_send_auth_error(backend->session->client, 1, 0, "Authorization failed on change_user");
|
||||
|
||||
} else {
|
||||
// get db name
|
||||
strcpy(database, (char *)client_auth_packet);
|
||||
|
||||
//fprintf(stderr, "<<<< Backend session data is [%s],[%s],[%s]\n", current_session->user, current_session->client_sha1, current_session->db);
|
||||
rv = gw_send_change_user_to_backend(database, username, client_sha1, backend_protocol);
|
||||
|
||||
/**
|
||||
* The current queue was not handled by func.write() in gw_send_change_user_to_backend()
|
||||
* We wrote a new gwbuf
|
||||
* Set backend command here!
|
||||
*/
|
||||
memcpy(&backend->command, &queue->command, sizeof(backend->command));
|
||||
|
||||
/**
|
||||
* Now copy new data into user session
|
||||
*/
|
||||
strcpy(current_session->user, username);
|
||||
strcpy(current_session->db, database);
|
||||
memcpy(current_session->client_sha1, client_sha1, sizeof(current_session->client_sha1));
|
||||
|
||||
//fprintf(stderr, ">>> The NEW Backend session data is [%s],[%s],[%s]: protocol state [%i]\n", current_session->user, current_session->client_sha1, current_session->db, backend_protocol->state);
|
||||
}
|
||||
|
||||
// consume all the data received from client
|
||||
len = gwbuf_length(queue);
|
||||
queue = gwbuf_consume(queue, len);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Session Change wrapper for func.write
|
||||
* The reply packet will be back routed to the right server
|
||||
* in the gw_read_backend_event checking the ROUTER_CHANGE_SESSION command in dcb->command
|
||||
*
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
static int gw_session(DCB *backend_dcb, void *data) {
|
||||
|
||||
GWBUF *queue = NULL;
|
||||
MySQLProtocol *backend_protocol = NULL;
|
||||
|
||||
backend_protocol = backend_dcb->protocol;
|
||||
queue = (GWBUF *) data;
|
||||
|
||||
queue->command = ROUTER_CHANGE_SESSION;
|
||||
|
||||
backend_dcb->func.write(backend_dcb, queue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
/////
|
||||
889
server/modules/protocol/mysql_client.c
Normal file
889
server/modules/protocol/mysql_client.c
Normal file
@ -0,0 +1,889 @@
|
||||
/*
|
||||
* This file is distributed as part of the SkySQL Gateway. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright SkySQL Ab 2013
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file mysql_client.c
|
||||
*
|
||||
* MySQL Protocol module for handling the protocol between the gateway
|
||||
* and the client.
|
||||
*
|
||||
* Revision History
|
||||
* Date Who Description
|
||||
* 14/06/2013 Mark Riddoch Initial version
|
||||
* 17/06/2013 Massimiliano Pinto Added Client To Gateway routines
|
||||
* 24/06/2013 Massimiliano Pinto Added: fetch passwords from service users' hashtable
|
||||
*/
|
||||
|
||||
#include <mysql_client_server_protocol.h>
|
||||
|
||||
static char *version_str = "V1.0.0";
|
||||
|
||||
static int gw_MySQLAccept(DCB *listener);
|
||||
static int gw_MySQLListener(DCB *listener, char *config_bind);
|
||||
static int gw_read_client_event(DCB* dcb);
|
||||
static int gw_write_client_event(DCB *dcb);
|
||||
static int gw_MySQLWrite_client(DCB *dcb, GWBUF *queue);
|
||||
static int gw_error_client_event(DCB *dcb);
|
||||
static int gw_client_close(DCB *dcb);
|
||||
static int gw_client_hangup_event(DCB *dcb);
|
||||
|
||||
int mysql_send_ok(DCB *dcb, int packet_number, int in_affected_rows, const char* mysql_message);
|
||||
int MySQLSendHandshake(DCB* dcb);
|
||||
static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue);
|
||||
|
||||
/*
|
||||
* The "module object" for the mysqld client protocol module.
|
||||
*/
|
||||
static GWPROTOCOL MyObject = {
|
||||
gw_read_client_event, /* Read - EPOLLIN handler */
|
||||
gw_MySQLWrite_client, /* Write - data from gateway */
|
||||
gw_write_client_event, /* WriteReady - EPOLLOUT handler */
|
||||
gw_error_client_event, /* Error - EPOLLERR handler */
|
||||
gw_client_hangup_event, /* HangUp - EPOLLHUP handler */
|
||||
gw_MySQLAccept, /* Accept */
|
||||
NULL, /* Connect */
|
||||
gw_client_close, /* Close */
|
||||
gw_MySQLListener, /* Listen */
|
||||
NULL, /* Authentication */
|
||||
NULL /* Session */
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of the mandatory version entry point
|
||||
*
|
||||
* @return version string of the module
|
||||
*/
|
||||
char *
|
||||
version()
|
||||
{
|
||||
return version_str;
|
||||
}
|
||||
|
||||
/**
|
||||
* The module initialisation routine, called when the module
|
||||
* is first loaded.
|
||||
*/
|
||||
void
|
||||
ModuleInit()
|
||||
{
|
||||
fprintf(stderr, "Initial MySQL Client Protcol module.\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* The module entry point routine. It is this routine that
|
||||
* must populate the structure that is referred to as the
|
||||
* "module object", this is a structure with the set of
|
||||
* external entry points for this module.
|
||||
*
|
||||
* @return The module object
|
||||
*/
|
||||
GWPROTOCOL *
|
||||
GetModuleObject()
|
||||
{
|
||||
return &MyObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* mysql_send_ok
|
||||
*
|
||||
* Send a MySQL protocol OK message to the dcb (client)
|
||||
*
|
||||
* @param dcb Descriptor Control Block for the connection to which the OK is sent
|
||||
* @param packet_number
|
||||
* @param in_affected_rows
|
||||
* @param mysql_message
|
||||
* @return packet length
|
||||
*
|
||||
*/
|
||||
int
|
||||
mysql_send_ok(DCB *dcb, int packet_number, int in_affected_rows, const char* mysql_message) {
|
||||
uint8_t *outbuf = NULL;
|
||||
uint8_t mysql_payload_size = 0;
|
||||
uint8_t mysql_packet_header[4];
|
||||
uint8_t *mysql_payload = NULL;
|
||||
uint8_t field_count = 0;
|
||||
uint8_t affected_rows = 0;
|
||||
uint8_t insert_id = 0;
|
||||
uint8_t mysql_server_status[2];
|
||||
uint8_t mysql_warning_count[2];
|
||||
GWBUF *buf;
|
||||
|
||||
affected_rows = in_affected_rows;
|
||||
|
||||
mysql_payload_size = sizeof(field_count) + sizeof(affected_rows) + sizeof(insert_id) + sizeof(mysql_server_status) + sizeof(mysql_warning_count);
|
||||
|
||||
if (mysql_message != NULL) {
|
||||
mysql_payload_size += strlen(mysql_message);
|
||||
}
|
||||
|
||||
// allocate memory for packet header + payload
|
||||
if ((buf = gwbuf_alloc(sizeof(mysql_packet_header) + mysql_payload_size)) == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
outbuf = GWBUF_DATA(buf);
|
||||
|
||||
// write packet header with packet number
|
||||
gw_mysql_set_byte3(mysql_packet_header, mysql_payload_size);
|
||||
mysql_packet_header[3] = packet_number;
|
||||
|
||||
// write header
|
||||
memcpy(outbuf, mysql_packet_header, sizeof(mysql_packet_header));
|
||||
|
||||
mysql_payload = outbuf + sizeof(mysql_packet_header);
|
||||
|
||||
mysql_server_status[0] = 2;
|
||||
mysql_server_status[1] = 0;
|
||||
mysql_warning_count[0] = 0;
|
||||
mysql_warning_count[1] = 0;
|
||||
|
||||
// write data
|
||||
memcpy(mysql_payload, &field_count, sizeof(field_count));
|
||||
mysql_payload = mysql_payload + sizeof(field_count);
|
||||
|
||||
memcpy(mysql_payload, &affected_rows, sizeof(affected_rows));
|
||||
mysql_payload = mysql_payload + sizeof(affected_rows);
|
||||
|
||||
memcpy(mysql_payload, &insert_id, sizeof(insert_id));
|
||||
mysql_payload = mysql_payload + sizeof(insert_id);
|
||||
|
||||
memcpy(mysql_payload, mysql_server_status, sizeof(mysql_server_status));
|
||||
mysql_payload = mysql_payload + sizeof(mysql_server_status);
|
||||
|
||||
memcpy(mysql_payload, mysql_warning_count, sizeof(mysql_warning_count));
|
||||
mysql_payload = mysql_payload + sizeof(mysql_warning_count);
|
||||
|
||||
if (mysql_message != NULL) {
|
||||
memcpy(mysql_payload, mysql_message, strlen(mysql_message));
|
||||
}
|
||||
|
||||
// writing data in the Client buffer queue
|
||||
dcb->func.write(dcb, buf);
|
||||
|
||||
return sizeof(mysql_packet_header) + mysql_payload_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* MySQLSendHandshake
|
||||
*
|
||||
* @param dcb The descriptor control block to use for sending the handshake request
|
||||
* @return The packet length sent
|
||||
*/
|
||||
int
|
||||
MySQLSendHandshake(DCB* dcb)
|
||||
{
|
||||
uint8_t *outbuf = NULL;
|
||||
uint8_t mysql_payload_size = 0;
|
||||
uint8_t mysql_packet_header[4];
|
||||
uint8_t mysql_packet_id = 0;
|
||||
uint8_t mysql_filler = GW_MYSQL_HANDSHAKE_FILLER;
|
||||
uint8_t mysql_protocol_version = GW_MYSQL_PROTOCOL_VERSION;
|
||||
uint8_t *mysql_handshake_payload = NULL;
|
||||
uint8_t mysql_thread_id[4];
|
||||
uint8_t mysql_scramble_buf[9] = "";
|
||||
uint8_t mysql_plugin_data[13] = "";
|
||||
uint8_t mysql_server_capabilities_one[2];
|
||||
uint8_t mysql_server_capabilities_two[2];
|
||||
uint8_t mysql_server_language = 8;
|
||||
uint8_t mysql_server_status[2];
|
||||
uint8_t mysql_scramble_len = 21;
|
||||
uint8_t mysql_filler_ten[10];
|
||||
uint8_t mysql_last_byte = 0x00;
|
||||
char server_scramble[GW_MYSQL_SCRAMBLE_SIZE + 1]="";
|
||||
|
||||
MySQLProtocol *protocol = DCB_PROTOCOL(dcb, MySQLProtocol);
|
||||
GWBUF *buf;
|
||||
|
||||
gw_generate_random_str(server_scramble, GW_MYSQL_SCRAMBLE_SIZE);
|
||||
|
||||
// copy back to the caller
|
||||
memcpy(protocol->scramble, server_scramble, GW_MYSQL_SCRAMBLE_SIZE);
|
||||
|
||||
// fill the handshake packet
|
||||
|
||||
memset(&mysql_filler_ten, 0x00, sizeof(mysql_filler_ten));
|
||||
|
||||
// thread id, now put thePID
|
||||
gw_mysql_set_byte4(mysql_thread_id, getpid() + dcb->fd);
|
||||
|
||||
memcpy(mysql_scramble_buf, server_scramble, 8);
|
||||
|
||||
memcpy(mysql_plugin_data, server_scramble + 8, 12);
|
||||
|
||||
mysql_payload_size = sizeof(mysql_protocol_version) + (strlen(GW_MYSQL_VERSION) + 1) + sizeof(mysql_thread_id) + 8 + sizeof(mysql_filler) + sizeof(mysql_server_capabilities_one) + sizeof(mysql_server_language) + sizeof(mysql_server_status) + sizeof(mysql_server_capabilities_two) + sizeof(mysql_scramble_len) + sizeof(mysql_filler_ten) + 12 + sizeof(mysql_last_byte) + strlen("mysql_native_password") + sizeof(mysql_last_byte);
|
||||
|
||||
// allocate memory for packet header + payload
|
||||
if ((buf = gwbuf_alloc(sizeof(mysql_packet_header) + mysql_payload_size)) == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
outbuf = GWBUF_DATA(buf);
|
||||
|
||||
// write packet heder with mysql_payload_size
|
||||
gw_mysql_set_byte3(mysql_packet_header, mysql_payload_size);
|
||||
//mysql_packet_header[0] = mysql_payload_size;
|
||||
|
||||
// write packent number, now is 0
|
||||
mysql_packet_header[3]= mysql_packet_id;
|
||||
memcpy(outbuf, mysql_packet_header, sizeof(mysql_packet_header));
|
||||
|
||||
// current buffer pointer
|
||||
mysql_handshake_payload = outbuf + sizeof(mysql_packet_header);
|
||||
|
||||
// write protocol version
|
||||
memcpy(mysql_handshake_payload, &mysql_protocol_version, sizeof(mysql_protocol_version));
|
||||
mysql_handshake_payload = mysql_handshake_payload + sizeof(mysql_protocol_version);
|
||||
|
||||
// write server version plus 0 filler
|
||||
strcpy((char *)mysql_handshake_payload, GW_MYSQL_VERSION);
|
||||
mysql_handshake_payload = mysql_handshake_payload + strlen(GW_MYSQL_VERSION);
|
||||
*mysql_handshake_payload = 0x00;
|
||||
|
||||
mysql_handshake_payload++;
|
||||
|
||||
// write thread id
|
||||
memcpy(mysql_handshake_payload, mysql_thread_id, sizeof(mysql_thread_id));
|
||||
mysql_handshake_payload = mysql_handshake_payload + sizeof(mysql_thread_id);
|
||||
|
||||
// write scramble buf
|
||||
memcpy(mysql_handshake_payload, mysql_scramble_buf, 8);
|
||||
mysql_handshake_payload = mysql_handshake_payload + 8;
|
||||
*mysql_handshake_payload = GW_MYSQL_HANDSHAKE_FILLER;
|
||||
mysql_handshake_payload++;
|
||||
|
||||
// write server capabilities part one
|
||||
mysql_server_capabilities_one[0] = GW_MYSQL_SERVER_CAPABILITIES_BYTE1;
|
||||
mysql_server_capabilities_one[1] = GW_MYSQL_SERVER_CAPABILITIES_BYTE2;
|
||||
|
||||
|
||||
mysql_server_capabilities_one[0] &= ~GW_MYSQL_CAPABILITIES_COMPRESS;
|
||||
mysql_server_capabilities_one[0] &= ~GW_MYSQL_CAPABILITIES_SSL;
|
||||
|
||||
memcpy(mysql_handshake_payload, mysql_server_capabilities_one, sizeof(mysql_server_capabilities_one));
|
||||
mysql_handshake_payload = mysql_handshake_payload + sizeof(mysql_server_capabilities_one);
|
||||
|
||||
// write server language
|
||||
memcpy(mysql_handshake_payload, &mysql_server_language, sizeof(mysql_server_language));
|
||||
mysql_handshake_payload = mysql_handshake_payload + sizeof(mysql_server_language);
|
||||
|
||||
//write server status
|
||||
mysql_server_status[0] = 2;
|
||||
mysql_server_status[1] = 0;
|
||||
memcpy(mysql_handshake_payload, mysql_server_status, sizeof(mysql_server_status));
|
||||
mysql_handshake_payload = mysql_handshake_payload + sizeof(mysql_server_status);
|
||||
|
||||
//write server capabilities part two
|
||||
mysql_server_capabilities_two[0] = 15;
|
||||
mysql_server_capabilities_two[1] = 128;
|
||||
|
||||
memcpy(mysql_handshake_payload, mysql_server_capabilities_two, sizeof(mysql_server_capabilities_two));
|
||||
mysql_handshake_payload = mysql_handshake_payload + sizeof(mysql_server_capabilities_two);
|
||||
|
||||
// write scramble_len
|
||||
memcpy(mysql_handshake_payload, &mysql_scramble_len, sizeof(mysql_scramble_len));
|
||||
mysql_handshake_payload = mysql_handshake_payload + sizeof(mysql_scramble_len);
|
||||
|
||||
//write 10 filler
|
||||
memcpy(mysql_handshake_payload, mysql_filler_ten, sizeof(mysql_filler_ten));
|
||||
mysql_handshake_payload = mysql_handshake_payload + sizeof(mysql_filler_ten);
|
||||
|
||||
// write plugin data
|
||||
memcpy(mysql_handshake_payload, mysql_plugin_data, 12);
|
||||
mysql_handshake_payload = mysql_handshake_payload + 12;
|
||||
|
||||
//write last byte, 0
|
||||
*mysql_handshake_payload = 0x00;
|
||||
mysql_handshake_payload++;
|
||||
|
||||
// to be understanded ????
|
||||
memcpy(mysql_handshake_payload, "mysql_native_password", strlen("mysql_native_password"));
|
||||
mysql_handshake_payload = mysql_handshake_payload + strlen("mysql_native_password");
|
||||
|
||||
//write last byte, 0
|
||||
*mysql_handshake_payload = 0x00;
|
||||
|
||||
mysql_handshake_payload++;
|
||||
|
||||
// writing data in the Client buffer queue
|
||||
dcb->func.write(dcb, buf);
|
||||
|
||||
return sizeof(mysql_packet_header) + mysql_payload_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* gw_mysql_do_authentication
|
||||
*
|
||||
* Performs the MySQL protocol 4.1 authentication, using data in GWBUF *queue
|
||||
*
|
||||
* The useful data: user, db, client_sha1 are copied into the MYSQL_session * dcb->session->data
|
||||
* client_capabilitiesa are copied into the dcb->protocol
|
||||
*
|
||||
* @param dcb Descriptor Control Block of the client
|
||||
* @param queue The GWBUF with data from client
|
||||
* @return 0 for Authentication ok, !=1 for failed autht
|
||||
*
|
||||
*/
|
||||
|
||||
static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
|
||||
MySQLProtocol *protocol = NULL;
|
||||
int compress = -1;
|
||||
int connect_with_db = -1;
|
||||
uint8_t *client_auth_packet = GWBUF_DATA(queue);
|
||||
char *username = NULL;
|
||||
char *database = NULL;
|
||||
unsigned int auth_token_len = 0;
|
||||
uint8_t *auth_token = NULL;
|
||||
uint8_t *stage1_hash = NULL;
|
||||
int auth_ret = -1;
|
||||
MYSQL_session *client_data = NULL;
|
||||
|
||||
if (dcb)
|
||||
protocol = DCB_PROTOCOL(dcb, MySQLProtocol);
|
||||
|
||||
client_data = (MYSQL_session *)calloc(1, sizeof(MYSQL_session));
|
||||
dcb->data = client_data;
|
||||
|
||||
stage1_hash = client_data->client_sha1;
|
||||
username = client_data->user;
|
||||
|
||||
memcpy(&protocol->client_capabilities, client_auth_packet + 4, 4);
|
||||
|
||||
connect_with_db = GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB & gw_mysql_get_byte4(&protocol->client_capabilities);
|
||||
compress = GW_MYSQL_CAPABILITIES_COMPRESS & gw_mysql_get_byte4(&protocol->client_capabilities);
|
||||
|
||||
// now get the user
|
||||
strcpy(username, (char *)(client_auth_packet + 4 + 4 + 4 + 1 + 23));
|
||||
fprintf(stderr, "<<< Client username is [%s]\n", username);
|
||||
|
||||
// get the auth token len
|
||||
memcpy(&auth_token_len, client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(username) + 1, 1);
|
||||
|
||||
if (connect_with_db) {
|
||||
database = client_data->db;
|
||||
strcpy(database, (char *)(client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(username) + 1 + 1 + auth_token_len));
|
||||
fprintf(stderr, "<<< Client selected db is [%s]\n", database);
|
||||
} else {
|
||||
fprintf(stderr, "<<< Client is NOT connected with db\n");
|
||||
}
|
||||
|
||||
// allocate memory for token only if auth_token_len > 0
|
||||
if (auth_token_len) {
|
||||
auth_token = (uint8_t *)malloc(auth_token_len);
|
||||
memcpy(auth_token, client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(username) + 1 + 1, auth_token_len);
|
||||
}
|
||||
|
||||
// decode the token and check the password
|
||||
// Note: if auth_token_len == 0 && auth_token == NULL, user is without password
|
||||
auth_ret = gw_check_mysql_scramble_data(dcb, auth_token, auth_token_len, protocol->scramble, sizeof(protocol->scramble), username, stage1_hash);
|
||||
|
||||
// let's free the auth_token now
|
||||
if (auth_token)
|
||||
free(auth_token);
|
||||
|
||||
if (auth_ret != 0) {
|
||||
fprintf(stderr, "<<< CLIENT AUTH FAILEDi for user [%s]\n", username);
|
||||
}
|
||||
|
||||
return auth_ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write function for client DCB: writes data from Gateway to Client
|
||||
*
|
||||
* @param dcb The DCB of the client
|
||||
* @param queue Queue of buffers to write
|
||||
*/
|
||||
int
|
||||
gw_MySQLWrite_client(DCB *dcb, GWBUF *queue)
|
||||
{
|
||||
int w, saved_errno = 0;
|
||||
|
||||
spinlock_acquire(&dcb->writeqlock);
|
||||
if (dcb->writeq)
|
||||
{
|
||||
/*
|
||||
* We have some queued data, so add our data to
|
||||
* the write queue and return.
|
||||
* The assumption is that there will be an EPOLLOUT
|
||||
* event to drain what is already queued. We are protected
|
||||
* by the spinlock, which will also be acquired by the
|
||||
* the routine that drains the queue data, so we should
|
||||
* not have a race condition on the event.
|
||||
*/
|
||||
dcb->writeq = gwbuf_append(dcb->writeq, queue);
|
||||
dcb->stats.n_buffered++;
|
||||
}
|
||||
else
|
||||
{
|
||||
int len;
|
||||
|
||||
/*
|
||||
* Loop over the buffer chain that has been passed to us
|
||||
* from the reading side.
|
||||
* Send as much of the data in that chain as possible and
|
||||
* add any balance to the write queue.
|
||||
*/
|
||||
while (queue != NULL)
|
||||
{
|
||||
len = GWBUF_LENGTH(queue);
|
||||
GW_NOINTR_CALL(w = write(dcb->fd, GWBUF_DATA(queue), len); dcb->stats.n_writes++);
|
||||
saved_errno = errno;
|
||||
if (w < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pull the number of bytes we have written from
|
||||
* queue with have.
|
||||
*/
|
||||
queue = gwbuf_consume(queue, w);
|
||||
if (w < len)
|
||||
{
|
||||
/* We didn't write all the data */
|
||||
}
|
||||
}
|
||||
/* Buffer the balance of any data */
|
||||
dcb->writeq = queue;
|
||||
if (queue)
|
||||
{
|
||||
dcb->stats.n_buffered++;
|
||||
}
|
||||
}
|
||||
spinlock_release(&dcb->writeqlock);
|
||||
|
||||
if (queue && (saved_errno != EAGAIN || saved_errno != EWOULDBLOCK))
|
||||
{
|
||||
/* We had a real write failure that we must deal with */
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Client read event triggered by EPOLLIN
|
||||
*
|
||||
* @param dcb Descriptor control block
|
||||
* @return TRUE on error
|
||||
*/
|
||||
int gw_read_client_event(DCB* dcb) {
|
||||
SESSION *session = NULL;
|
||||
ROUTER_OBJECT *router = NULL;
|
||||
ROUTER *router_instance = NULL;
|
||||
void *rsession = NULL;
|
||||
MySQLProtocol *protocol = NULL;
|
||||
int b = -1;
|
||||
|
||||
if (dcb) {
|
||||
protocol = DCB_PROTOCOL(dcb, MySQLProtocol);
|
||||
}
|
||||
|
||||
if (ioctl(dcb->fd, FIONREAD, &b)) {
|
||||
fprintf(stderr, "Client Ioctl FIONREAD error for %i: errno %i, %s\n", dcb->fd, errno , strerror(errno));
|
||||
return 1;
|
||||
} else {
|
||||
//fprintf(stderr, "Client IOCTL FIONREAD bytes to read = %i\n", b);
|
||||
}
|
||||
|
||||
switch (protocol->state) {
|
||||
case MYSQL_AUTH_SENT:
|
||||
|
||||
/*
|
||||
* Read all the data that is available into a chain of buffers
|
||||
*/
|
||||
{
|
||||
int len = -1;
|
||||
int ret = -1;
|
||||
GWBUF *queue = NULL;
|
||||
GWBUF *gw_buffer = NULL;
|
||||
int auth_val = -1;
|
||||
//////////////////////////////////////////////////////
|
||||
// read and handle errors & close, or return if busyA
|
||||
// note: if b == 0 error handling is not triggered, just return
|
||||
// without closing
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
if ((ret = gw_read_gwbuff(dcb, &gw_buffer, b)) != 0)
|
||||
return ret;
|
||||
|
||||
// example with consume, assuming one buffer only ...
|
||||
queue = gw_buffer;
|
||||
len = GWBUF_LENGTH(queue);
|
||||
|
||||
//fprintf(stderr, "<<< Reading from Client %i bytes: [%s]\n", len, GWBUF_DATA(queue));
|
||||
|
||||
auth_val = gw_mysql_do_authentication(dcb, queue);
|
||||
|
||||
// Data handled withot the dcb->func.write
|
||||
// so consume it now
|
||||
// be sure to consume it all
|
||||
queue = gwbuf_consume(queue, len);
|
||||
|
||||
if (auth_val == 0)
|
||||
{
|
||||
SESSION *session = NULL;
|
||||
|
||||
protocol->state = MYSQL_AUTH_RECV;
|
||||
|
||||
//write to client mysql AUTH_OK packet, packet n. is 2
|
||||
mysql_send_ok(dcb, 2, 0, NULL);
|
||||
|
||||
// start a new session, and connect to backends
|
||||
session = session_alloc(dcb->service, dcb);
|
||||
|
||||
protocol->state = MYSQL_IDLE;
|
||||
|
||||
session->data = (MYSQL_session *)dcb->data;
|
||||
}
|
||||
else
|
||||
{
|
||||
protocol->state = MYSQL_AUTH_FAILED;
|
||||
|
||||
mysql_send_auth_error(dcb, 2, 0, "Authorization failed");
|
||||
|
||||
dcb->func.close(dcb);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
|
||||
case MYSQL_IDLE:
|
||||
case MYSQL_WAITING_RESULT:
|
||||
/*
|
||||
* Read all the data that is available into a chain of buffers
|
||||
*/
|
||||
{
|
||||
int len;
|
||||
GWBUF *queue = NULL;
|
||||
GWBUF *gw_buffer = NULL;
|
||||
uint8_t *ptr_buff = NULL;
|
||||
int mysql_command = -1;
|
||||
int ret = -1;
|
||||
|
||||
session = dcb->session;
|
||||
|
||||
// get the backend session, if available
|
||||
if (session) {
|
||||
router = session->service->router;
|
||||
router_instance = session->service->router_instance;
|
||||
rsession = session->router_session;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// read and handle errors & close, or return if busy
|
||||
//////////////////////////////////////////////////////
|
||||
if ((ret = gw_read_gwbuff(dcb, &gw_buffer, b)) != 0)
|
||||
return ret;
|
||||
|
||||
/* Now, we are assuming in the first buffer there is the information form mysql command */
|
||||
|
||||
queue = gw_buffer;
|
||||
len = GWBUF_LENGTH(queue);
|
||||
|
||||
ptr_buff = GWBUF_DATA(queue);
|
||||
|
||||
/* get mysql commang at fourth byte */
|
||||
if (ptr_buff)
|
||||
mysql_command = ptr_buff[4];
|
||||
|
||||
if (mysql_command == '\x03') {
|
||||
/// this is a standard MySQL query !!!!
|
||||
}
|
||||
|
||||
/**
|
||||
* Routing Client input to Backend
|
||||
*/
|
||||
|
||||
/* Do not route the query without session! */
|
||||
if(!rsession) {
|
||||
if (mysql_command == '\x01') {
|
||||
/* COM_QUIT handling */
|
||||
//fprintf(stderr, "COM_QUIT received with no connected backends from %i\n", dcb->fd);
|
||||
(dcb->func).close(dcb);
|
||||
|
||||
return 1;
|
||||
} else {
|
||||
/* Send a custom error as MySQL command reply */
|
||||
mysql_send_custom_error(dcb, 1, 0, "Connection to backend lost");
|
||||
|
||||
protocol->state = MYSQL_IDLE;
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* We can route the query */
|
||||
|
||||
/* COM_QUIT handling */
|
||||
if (mysql_command == '\x01') {
|
||||
//fprintf(stderr, "COM_QUIT received from %i and passed to backed\n", dcb->fd);
|
||||
|
||||
/* this will propagate COM_QUIT to backend(s) */
|
||||
//fprintf(stderr, "<<< Routing the COM_QUIT ...\n");
|
||||
router->routeQuery(router_instance, rsession, queue);
|
||||
|
||||
/* close client connection */
|
||||
(dcb->func).close(dcb);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* MySQL Command Routing */
|
||||
|
||||
protocol->state = MYSQL_ROUTING;
|
||||
|
||||
/* writing in the backend buffer queue, via routeQuery */
|
||||
|
||||
//fprintf(stderr, "<<< Routing the Query ...\n");
|
||||
router->routeQuery(router_instance, rsession, queue);
|
||||
|
||||
protocol->state = MYSQL_WAITING_RESULT;
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// todo
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////
|
||||
// client write event to Client triggered by EPOLLOUT
|
||||
//////////////////////////////////////////////
|
||||
int gw_write_client_event(DCB *dcb) {
|
||||
MySQLProtocol *protocol = NULL;
|
||||
|
||||
if (dcb == NULL) {
|
||||
fprintf(stderr, "DCB is NULL, return\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (dcb->state == DCB_STATE_DISCONNECTED) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (dcb->protocol) {
|
||||
protocol = DCB_PROTOCOL(dcb, MySQLProtocol);
|
||||
} else {
|
||||
fprintf(stderr, "DCB protocol is NULL, return\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((protocol->state == MYSQL_IDLE) || (protocol->state == MYSQL_WAITING_RESULT)) {
|
||||
int w;
|
||||
|
||||
w = dcb_drain_writeq(dcb);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
///
|
||||
// set listener for mysql protocol, retur 1 on success and 0 in failure
|
||||
///
|
||||
int gw_MySQLListener(DCB *listener, char *config_bind) {
|
||||
int l_so;
|
||||
struct sockaddr_in serv_addr;
|
||||
char *bind_address_and_port = NULL;
|
||||
char *p;
|
||||
char address[1024]="";
|
||||
int port=0;
|
||||
int one = 1;
|
||||
|
||||
// this gateway, as default, will bind on port 4404 for localhost only
|
||||
(config_bind != NULL) ? (bind_address_and_port = config_bind) : (bind_address_and_port = "127.0.0.1:4406");
|
||||
|
||||
listener->fd = -1;
|
||||
|
||||
memset(&serv_addr, 0, sizeof serv_addr);
|
||||
serv_addr.sin_family = AF_INET;
|
||||
|
||||
p = strchr(bind_address_and_port, ':');
|
||||
if (p) {
|
||||
strncpy(address, bind_address_and_port, sizeof(address));
|
||||
address[sizeof(address)-1] = '\0';
|
||||
p = strchr(address, ':');
|
||||
*p = '\0';
|
||||
port = atoi(p+1);
|
||||
setipaddress(&serv_addr.sin_addr, address);
|
||||
|
||||
snprintf(address, (sizeof(address) - 1), "%s", inet_ntoa(serv_addr.sin_addr));
|
||||
} else {
|
||||
port = atoi(bind_address_and_port);
|
||||
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
sprintf(address, "0.0.0.0");
|
||||
}
|
||||
|
||||
serv_addr.sin_port = htons(port);
|
||||
|
||||
// socket create
|
||||
if ((l_so = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
fprintf(stderr, ">>> Error: can't open listening socket. Errno %i, %s\n", errno, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// socket options
|
||||
setsockopt(l_so, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one));
|
||||
|
||||
// set NONBLOCKING mode
|
||||
setnonblocking(l_so);
|
||||
|
||||
// bind address and port
|
||||
if (bind(l_so, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
|
||||
fprintf(stderr, ">>> Bind failed !!! %i, [%s]\n", errno, strerror(errno));
|
||||
fprintf(stderr, ">>> can't bind to address and port");
|
||||
return 0;
|
||||
}
|
||||
|
||||
fprintf(stderr, ">> GATEWAY bind is: %s:%i. FD is %i\n", address, port, l_so);
|
||||
|
||||
listen(l_so, 10 * SOMAXCONN);
|
||||
|
||||
fprintf(stderr, ">> GATEWAY listen backlog queue is %i\n", 10 * SOMAXCONN);
|
||||
|
||||
listener->state = DCB_STATE_IDLE;
|
||||
|
||||
// assign l_so to dcb
|
||||
listener->fd = l_so;
|
||||
|
||||
// add listening socket to poll structure
|
||||
if (poll_add_dcb(listener) == -1) {
|
||||
fprintf(stderr, ">>> poll_add_dcb: can't add the listen_sock! Errno %i, %s\n", errno, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
listener->func.accept = gw_MySQLAccept;
|
||||
|
||||
listener->state = DCB_STATE_LISTENING;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int gw_MySQLAccept(DCB *listener) {
|
||||
|
||||
fprintf(stderr, "MySQL Listener socket is: %i\n", listener->fd);
|
||||
|
||||
while (1) {
|
||||
int c_sock;
|
||||
struct sockaddr_in local;
|
||||
socklen_t addrlen;
|
||||
addrlen = sizeof(local);
|
||||
DCB *client;
|
||||
MySQLProtocol *protocol;
|
||||
int sendbuf = GW_BACKEND_SO_SNDBUF;
|
||||
socklen_t optlen = sizeof(sendbuf);
|
||||
|
||||
// new connection from client
|
||||
c_sock = accept(listener->fd, (struct sockaddr *) &local, &addrlen);
|
||||
|
||||
if (c_sock == -1) {
|
||||
if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
|
||||
/* We have processed all incoming connections. */
|
||||
break;
|
||||
} else {
|
||||
fprintf(stderr, "Accept error for %i, Err: %i, %s\n", listener->fd, errno, strerror(errno));
|
||||
// what else to do?
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
listener->stats.n_accepts++;
|
||||
|
||||
fprintf(stderr, "Processing %i connection fd %i for listener %i\n", listener->stats.n_accepts, c_sock, listener->fd);
|
||||
// set nonblocking
|
||||
|
||||
setsockopt(c_sock, SOL_SOCKET, SO_SNDBUF, &sendbuf, optlen);
|
||||
setnonblocking(c_sock);
|
||||
|
||||
client = dcb_alloc();
|
||||
client->service = listener->session->service;
|
||||
client->fd = c_sock;
|
||||
client->remote = strdup(inet_ntoa(local.sin_addr));
|
||||
|
||||
protocol = (MySQLProtocol *) calloc(1, sizeof(MySQLProtocol));
|
||||
client->protocol = (void *)protocol;
|
||||
|
||||
|
||||
protocol->state = MYSQL_ALLOC;
|
||||
protocol->descriptor = client;
|
||||
protocol->fd = c_sock;
|
||||
|
||||
// assign function poiters to "func" field
|
||||
memcpy(&client->func, &MyObject, sizeof(GWPROTOCOL));
|
||||
|
||||
client->state = DCB_STATE_IDLE;
|
||||
|
||||
// event install
|
||||
if (poll_add_dcb(client) == -1) {
|
||||
perror("poll_add_dcb: conn_sock");
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
//fprintf(stderr, "Added fd %i to poll, protocol state [%i]\n", c_sock , client->state);
|
||||
client->state = DCB_STATE_POLLING;
|
||||
}
|
||||
|
||||
client->state = DCB_STATE_PROCESSING;
|
||||
|
||||
//send handshake to the client
|
||||
MySQLSendHandshake(client);
|
||||
|
||||
// client protocol state change
|
||||
protocol->state = MYSQL_AUTH_SENT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*/
|
||||
static int gw_error_client_event(DCB *dcb) {
|
||||
//fprintf(stderr, "#### Handle error function gw_error_client_event, for [%i] is [%s]\n", dcb->fd, gw_dcb_state2string(dcb->state));
|
||||
//dcb_close(dcb);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
gw_client_close(DCB *dcb)
|
||||
{
|
||||
dcb_close(dcb);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a hangup event on the client side descriptor.
|
||||
*
|
||||
* We simply close the DCB, this will propogate the closure to any
|
||||
* backend descriptors and perform the session cleanup.
|
||||
*
|
||||
* @param dcb The DCB of the connection
|
||||
*/
|
||||
static int
|
||||
gw_client_hangup_event(DCB *dcb)
|
||||
{
|
||||
dcb_close(dcb);
|
||||
return 1;
|
||||
}
|
||||
1054
server/modules/protocol/mysql_common.c
Normal file
1054
server/modules/protocol/mysql_common.c
Normal file
File diff suppressed because it is too large
Load Diff
399
server/modules/protocol/telnetd.c
Normal file
399
server/modules/protocol/telnetd.c
Normal file
@ -0,0 +1,399 @@
|
||||
/*
|
||||
* This file is distributed as part of the SkySQL Gateway. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright SkySQL Ab 2013
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <dcb.h>
|
||||
#include <buffer.h>
|
||||
#include <service.h>
|
||||
#include <session.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <router.h>
|
||||
#include <poll.h>
|
||||
#include <atomic.h>
|
||||
#include <gw.h>
|
||||
#include <telnetd.h>
|
||||
#include <adminusers.h>
|
||||
|
||||
/**
|
||||
* @file telnetd.c - telnet daemon protocol module
|
||||
*
|
||||
* The telnetd protocol module is intended as a mechanism to allow connections
|
||||
* into the gateway for the purpsoe of accessing debugging information within
|
||||
* the gateway rather than a protocol to be used to send queries to backend
|
||||
* databases.
|
||||
*
|
||||
* In the first instance it is intended to allow a debug connection to access
|
||||
* internal data structures, however it may also be used to manage the
|
||||
* configuration of the gateway.
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
* Date Who Description
|
||||
* 17/06/2013 Mark Riddoch Initial version
|
||||
* 17/07/2013 Mark Riddoch Addition of login phase
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
static char *version_str = "V1.0.1";
|
||||
|
||||
static int telnetd_read_event(DCB* dcb);
|
||||
static int telnetd_write_event(DCB *dcb);
|
||||
static int telnetd_write(DCB *dcb, GWBUF *queue);
|
||||
static int telnetd_error(DCB *dcb);
|
||||
static int telnetd_hangup(DCB *dcb);
|
||||
static int telnetd_accept(DCB *dcb);
|
||||
static int telnetd_close(DCB *dcb);
|
||||
static int telnetd_listen(DCB *dcb, char *config);
|
||||
|
||||
/**
|
||||
* The "module object" for the telnetd protocol module.
|
||||
*/
|
||||
static GWPROTOCOL MyObject = {
|
||||
telnetd_read_event, /**< Read - EPOLLIN handler */
|
||||
telnetd_write, /**< Write - data from gateway */
|
||||
telnetd_write_event, /**< WriteReady - EPOLLOUT handler */
|
||||
telnetd_error, /**< Error - EPOLLERR handler */
|
||||
telnetd_hangup, /**< HangUp - EPOLLHUP handler */
|
||||
telnetd_accept, /**< Accept */
|
||||
NULL, /**< Connect */
|
||||
telnetd_close, /**< Close */
|
||||
telnetd_listen, /**< Create a listener */
|
||||
NULL, /**< Authentication */
|
||||
NULL /**< Session */
|
||||
};
|
||||
|
||||
static void telnetd_command(DCB *, unsigned char *cmd);
|
||||
static void telnetd_echo(DCB *dcb, int enable);
|
||||
|
||||
/**
|
||||
* Implementation of the mandatory version entry point
|
||||
*
|
||||
* @return version string of the module
|
||||
*/
|
||||
char *
|
||||
version()
|
||||
{
|
||||
return version_str;
|
||||
}
|
||||
|
||||
/**
|
||||
* The module initialisation routine, called when the module
|
||||
* is first loaded.
|
||||
*/
|
||||
void
|
||||
ModuleInit()
|
||||
{
|
||||
fprintf(stderr, "Initialise Telnetd Protocol module.\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* The module entry point routine. It is this routine that
|
||||
* must populate the structure that is referred to as the
|
||||
* "module object", this is a structure with the set of
|
||||
* external entry points for this module.
|
||||
*
|
||||
* @return The module object
|
||||
*/
|
||||
GWPROTOCOL *
|
||||
GetModuleObject()
|
||||
{
|
||||
return &MyObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read event for EPOLLIN on the telnetd protocol module.
|
||||
*
|
||||
* @param dcb The descriptor control block
|
||||
* @return
|
||||
*/
|
||||
static int
|
||||
telnetd_read_event(DCB* dcb)
|
||||
{
|
||||
int n;
|
||||
GWBUF *head = NULL;
|
||||
SESSION *session = dcb->session;
|
||||
ROUTER_OBJECT *router = session->service->router;
|
||||
ROUTER *router_instance = session->service->router_instance;
|
||||
void *rsession = session->router_session;
|
||||
TELNETD *telnetd = (TELNETD *)dcb->protocol;
|
||||
char *password, *t;
|
||||
|
||||
if ((n = dcb_read(dcb, &head)) != -1)
|
||||
{
|
||||
dcb->state = DCB_STATE_PROCESSING;
|
||||
if (head)
|
||||
{
|
||||
unsigned char *ptr = GWBUF_DATA(head);
|
||||
ptr = GWBUF_DATA(head);
|
||||
while (*ptr == TELNET_IAC)
|
||||
{
|
||||
telnetd_command(dcb, ptr + 1);
|
||||
GWBUF_CONSUME(head, 3);
|
||||
ptr = GWBUF_DATA(head);
|
||||
}
|
||||
if (GWBUF_LENGTH(head))
|
||||
{
|
||||
switch (telnetd->state)
|
||||
{
|
||||
case TELNETD_STATE_LOGIN:
|
||||
telnetd->username = strndup(GWBUF_DATA(head), GWBUF_LENGTH(head));
|
||||
/* Strip the cr/lf from the username */
|
||||
t = strstr(telnetd->username, "\r\n");
|
||||
if (t)
|
||||
*t = 0;
|
||||
telnetd->state = TELNETD_STATE_PASSWD;
|
||||
dcb_printf(dcb, "Password: ");
|
||||
telnetd_echo(dcb, 0);
|
||||
GWBUF_CONSUME(head, GWBUF_LENGTH(head));
|
||||
break;
|
||||
case TELNETD_STATE_PASSWD:
|
||||
password = strndup(GWBUF_DATA(head), GWBUF_LENGTH(head));
|
||||
/* Strip the cr/lf from the username */
|
||||
t = strstr(password, "\r\n");
|
||||
if (t)
|
||||
*t = 0;
|
||||
if (admin_verify(telnetd->username, password))
|
||||
{
|
||||
telnetd_echo(dcb, 1);
|
||||
telnetd->state = TELNETD_STATE_DATA;
|
||||
dcb_printf(dcb, "\n\nMaxScale> ");
|
||||
}
|
||||
else
|
||||
{
|
||||
dcb_printf(dcb, "\n\rLogin incorrect\n\rLogin: ");
|
||||
telnetd_echo(dcb, 1);
|
||||
telnetd->state = TELNETD_STATE_LOGIN;
|
||||
}
|
||||
GWBUF_CONSUME(head, GWBUF_LENGTH(head));
|
||||
free(password);
|
||||
break;
|
||||
case TELNETD_STATE_DATA:
|
||||
router->routeQuery(router_instance, rsession, head);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dcb->state = DCB_STATE_POLLING;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* EPOLLOUT handler for the telnetd protocol module.
|
||||
*
|
||||
* @param dcb The descriptor control block
|
||||
* @return
|
||||
*/
|
||||
static int
|
||||
telnetd_write_event(DCB *dcb)
|
||||
{
|
||||
return dcb_drain_writeq(dcb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write routine for the telnetd protocol module.
|
||||
*
|
||||
* Writes the content of the buffer queue to the socket
|
||||
* observing the non-blocking principles of the gateway.
|
||||
*
|
||||
* @param dcb Descriptor Control Block for the socket
|
||||
* @param queue Linked list of buffes to write
|
||||
*/
|
||||
static int
|
||||
telnetd_write(DCB *dcb, GWBUF *queue)
|
||||
{
|
||||
return dcb_write(dcb, queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the EPOLLERR event.
|
||||
*
|
||||
* @param dcb The descriptor control block
|
||||
*/
|
||||
static int
|
||||
telnetd_error(DCB *dcb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the EPOLLHUP event.
|
||||
*
|
||||
* @param dcb The descriptor control block
|
||||
*/
|
||||
static int
|
||||
telnetd_hangup(DCB *dcb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the EPOLLIN event when the DCB refers to the listening
|
||||
* socket for the protocol.
|
||||
*
|
||||
* @param dcb The descriptor control block
|
||||
*/
|
||||
static int
|
||||
telnetd_accept(DCB *dcb)
|
||||
{
|
||||
int n_connect = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
int so;
|
||||
struct sockaddr_in addr;
|
||||
socklen_t addrlen;
|
||||
DCB *client;
|
||||
|
||||
if ((so = accept(dcb->fd, (struct sockaddr *)&addr, &addrlen)) == -1)
|
||||
return n_connect;
|
||||
else
|
||||
{
|
||||
atomic_add(&dcb->stats.n_accepts, 1);
|
||||
client = dcb_alloc();
|
||||
client->fd = so;
|
||||
client->remote = strdup(inet_ntoa(addr.sin_addr));
|
||||
memcpy(&client->func, &MyObject, sizeof(GWPROTOCOL));
|
||||
client->session = session_alloc(dcb->session->service, client);
|
||||
|
||||
client->state = DCB_STATE_IDLE;
|
||||
client->protocol = malloc(sizeof(TELNETD));
|
||||
|
||||
if (poll_add_dcb(client) == -1)
|
||||
{
|
||||
return n_connect;
|
||||
}
|
||||
n_connect++;
|
||||
|
||||
((TELNETD *)(client->protocol))->state = TELNETD_STATE_LOGIN;
|
||||
dcb_printf(client, "MaxScale login: ");
|
||||
client->state = DCB_STATE_POLLING;
|
||||
}
|
||||
}
|
||||
return n_connect;
|
||||
}
|
||||
|
||||
/**
|
||||
* The close handler for the descriptor. Called by the gateway to
|
||||
* explicitly close a connection.
|
||||
*
|
||||
* @param dcb The descriptor control block
|
||||
*/
|
||||
|
||||
static int
|
||||
telnetd_close(DCB *dcb)
|
||||
{
|
||||
dcb_close(dcb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Telnet daemon listener entry point
|
||||
*
|
||||
* @param listener The Listener DCB
|
||||
* @param config Configuration (ip:port)
|
||||
*/
|
||||
static int
|
||||
telnetd_listen(DCB *listener, char *config)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
char *port;
|
||||
int one = 1;
|
||||
short pnum;
|
||||
|
||||
memcpy(&listener->func, &MyObject, sizeof(GWPROTOCOL));
|
||||
|
||||
port = strrchr(config, ':');
|
||||
if (port)
|
||||
port++;
|
||||
else
|
||||
port = "4442";
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
pnum = atoi(port);
|
||||
addr.sin_port = htons(pnum);
|
||||
|
||||
if ((listener->fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// socket options
|
||||
setsockopt(listener->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one));
|
||||
// set NONBLOCKING mode
|
||||
setnonblocking(listener->fd);
|
||||
// bind address and port
|
||||
if (bind(listener->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
listener->state = DCB_STATE_LISTENING;
|
||||
listen(listener->fd, SOMAXCONN);
|
||||
|
||||
if (poll_add_dcb(listener) == -1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Telnet command implementation
|
||||
*
|
||||
* Called for each command in the telnet stream.
|
||||
*
|
||||
* Currently we do no command execution
|
||||
*
|
||||
* @param dcb The client DCB
|
||||
* @param cmd The command stream
|
||||
*/
|
||||
static void
|
||||
telnetd_command(DCB *dcb, unsigned char *cmd)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable telnet protocol echo
|
||||
*
|
||||
* @param dcb DCB of the telnet connection
|
||||
* @param enable Enable or disable echo functionality
|
||||
*/
|
||||
static void
|
||||
telnetd_echo(DCB *dcb, int enable)
|
||||
{
|
||||
GWBUF *gwbuf;
|
||||
char *buf;
|
||||
|
||||
if ((gwbuf = gwbuf_alloc(3)) == NULL)
|
||||
return;
|
||||
buf = GWBUF_DATA(gwbuf);
|
||||
buf[0] = TELNET_IAC;
|
||||
buf[1] = enable ? TELNET_WONT : TELNET_WILL;
|
||||
buf[2] = TELNET_ECHO;
|
||||
dcb_write(dcb, gwbuf);
|
||||
}
|
||||
85
server/modules/routing/Makefile
Normal file
85
server/modules/routing/Makefile
Normal file
@ -0,0 +1,85 @@
|
||||
# This file is distributed as part of the SkySQL Gateway. It is free
|
||||
# software: you can redistribute it and/or modify it under the terms of the
|
||||
# GNU General Public License as published by the Free Software Foundation,
|
||||
# version 2.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# Copyright SkySQL Ab 2013
|
||||
#
|
||||
# Revision History
|
||||
# Date Who Description
|
||||
# 13/06/13 Mark Riddoch Initial routing module development
|
||||
# 27/06/13 Vilho Raatikka Added logmanager-related libs and
|
||||
# headers so that liblog_manager.so can
|
||||
# be linked in.
|
||||
# 27/06/13 Mark Riddoch Addition of read write splitter
|
||||
|
||||
include ../../../build_gateway.inc
|
||||
|
||||
LOGPATH := $(ROOT_PATH)/log_manager
|
||||
UTILSPATH := $(ROOT_PATH)/utils
|
||||
|
||||
CC=cc
|
||||
CFLAGS=-c -fPIC -I/usr/include -I../include -I../../include -I$(LOGPATH) \
|
||||
-I$(UTILSPATH) -Wall -g
|
||||
|
||||
include ../../../makefile.inc
|
||||
|
||||
LDFLAGS=-shared -L$(LOGPATH) -Wl,-rpath,$(DEST)/lib \
|
||||
-Wl,-rpath,$(LOGPATH) -Wl,-rpath,$(UTILSPATH)
|
||||
|
||||
TESTSRCS=testroute.c
|
||||
TESTOBJ=$(TESTSRCS:.c=.o)
|
||||
READCONSRCS=readconnroute.c
|
||||
READCONOBJ=$(READCONSRCS:.c=.o)
|
||||
DEBUGCLISRCS=debugcli.c debugcmd.c
|
||||
DEBUGCLIOBJ=$(DEBUGCLISRCS:.c=.o)
|
||||
SRCS=$(TESTSRCS) $(READCONSRCS) $(DEBUGCLISRCS)
|
||||
OBJ=$(SRCS:.c=.o)
|
||||
LIBS=$(UTILSPATH)/skygw_utils.o -lssl -llog_manager
|
||||
MODULES= libdebugcli.so libreadconnroute.so libtestroute.so
|
||||
|
||||
|
||||
all: $(MODULES)
|
||||
|
||||
libtestroute.so: $(TESTOBJ)
|
||||
$(CC) $(LDFLAGS) $(TESTOBJ) $(LIBS) -o $@
|
||||
|
||||
libreadconnroute.so: $(READCONOBJ)
|
||||
$(CC) $(LDFLAGS) $(READCONOBJ) $(LIBS) -o $@
|
||||
|
||||
libdebugcli.so: $(DEBUGCLIOBJ)
|
||||
$(CC) $(LDFLAGS) $(DEBUGCLIOBJ) $(LIBS) -o $@
|
||||
|
||||
libreadwritesplit.so:
|
||||
# (cd readwritesplit; make; cp $@ ..)
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(OBJ) $(MODULES)
|
||||
(cd readwritesplit; make clean)
|
||||
|
||||
tags:
|
||||
ctags $(SRCS) $(HDRS)
|
||||
(cd readwritesplit; make tags)
|
||||
|
||||
depend:
|
||||
@rm -f depend.mk
|
||||
cc -M $(CFLAGS) $(SRCS) > depend.mk
|
||||
(cd readwritesplit; make depend)
|
||||
|
||||
install: $(MODULES)
|
||||
install -D $(MODULES) $(DEST)/MaxScale/modules
|
||||
(cd readwritesplit; make DEST=$(DEST) install)
|
||||
|
||||
include depend.mk
|
||||
247
server/modules/routing/debugcli.c
Normal file
247
server/modules/routing/debugcli.c
Normal file
@ -0,0 +1,247 @@
|
||||
/*
|
||||
* This file is distributed as part of the SkySQL Gateway. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright SkySQL Ab 2013
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file debugcli.c - A "routing module" that in fact merely gives
|
||||
* access to debug commands within the gateway
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 18/06/13 Mark Riddoch Initial implementation
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <service.h>
|
||||
#include <session.h>
|
||||
#include <router.h>
|
||||
#include <modules.h>
|
||||
#include <atomic.h>
|
||||
#include <spinlock.h>
|
||||
#include <dcb.h>
|
||||
#include <poll.h>
|
||||
#include <debugcli.h>
|
||||
#include <skygw_utils.h>
|
||||
#include <log_manager.h>
|
||||
|
||||
static char *version_str = "V1.0.1";
|
||||
|
||||
/* The router entry points */
|
||||
static ROUTER *createInstance(SERVICE *service, char **options);
|
||||
static void *newSession(ROUTER *instance, SESSION *session);
|
||||
static void closeSession(ROUTER *instance, void *router_session);
|
||||
static int execute(ROUTER *instance, void *router_session, GWBUF *queue);
|
||||
static void diagnostics(ROUTER *instance, DCB *dcb);
|
||||
|
||||
/** The module object definition */
|
||||
static ROUTER_OBJECT MyObject = { createInstance, newSession, closeSession, execute, diagnostics, NULL };
|
||||
|
||||
extern int execute_cmd(CLI_SESSION *cli);
|
||||
|
||||
static SPINLOCK instlock;
|
||||
static CLI_INSTANCE *instances;
|
||||
|
||||
/**
|
||||
* Implementation of the mandatory version entry point
|
||||
*
|
||||
* @return version string of the module
|
||||
*/
|
||||
char *
|
||||
version()
|
||||
{
|
||||
return version_str;
|
||||
}
|
||||
|
||||
/**
|
||||
* The module initialisation routine, called when the module
|
||||
* is first loaded.
|
||||
*/
|
||||
void
|
||||
ModuleInit()
|
||||
{
|
||||
skygw_log_write(NULL, LOGFILE_MESSAGE, "Initialise debug CLI router module %s.\n", version_str);
|
||||
spinlock_init(&instlock);
|
||||
instances = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* The module entry point routine. It is this routine that
|
||||
* must populate the structure that is referred to as the
|
||||
* "module object", this is a structure with the set of
|
||||
* external entry points for this module.
|
||||
*
|
||||
* @return The module object
|
||||
*/
|
||||
ROUTER_OBJECT *
|
||||
GetModuleObject()
|
||||
{
|
||||
return &MyObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the router for a particular service
|
||||
* within the gateway.
|
||||
*
|
||||
* @param service The service this router is being create for
|
||||
* @param options Any array of options for the query router
|
||||
*
|
||||
* @return The instance data for this new instance
|
||||
*/
|
||||
static ROUTER *
|
||||
createInstance(SERVICE *service, char **options)
|
||||
{
|
||||
CLI_INSTANCE *inst;
|
||||
|
||||
if ((inst = malloc(sizeof(CLI_INSTANCE))) == NULL)
|
||||
return NULL;
|
||||
|
||||
inst->service = service;
|
||||
spinlock_init(&inst->lock);
|
||||
inst->sessions = NULL;
|
||||
|
||||
|
||||
/*
|
||||
* We have completed the creation of the instance data, so now
|
||||
* insert this router instance into the linked list of routers
|
||||
* that have been created with this module.
|
||||
*/
|
||||
spinlock_acquire(&instlock);
|
||||
inst->next = instances;
|
||||
instances = inst;
|
||||
spinlock_release(&instlock);
|
||||
|
||||
return (ROUTER *)inst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate a new session with this instance of the router.
|
||||
*
|
||||
* @param instance The router instance data
|
||||
* @param session The session itself
|
||||
* @return Session specific data for this session
|
||||
*/
|
||||
static void *
|
||||
newSession(ROUTER *instance, SESSION *session)
|
||||
{
|
||||
CLI_INSTANCE *inst = (CLI_INSTANCE *)instance;
|
||||
CLI_SESSION *client;
|
||||
|
||||
if ((client = (CLI_SESSION *)malloc(sizeof(CLI_SESSION))) == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
client->session = session;
|
||||
|
||||
memset(client->cmdbuf, 0, 80);
|
||||
|
||||
spinlock_acquire(&inst->lock);
|
||||
client->next = inst->sessions;
|
||||
inst->sessions = client;
|
||||
spinlock_release(&inst->lock);
|
||||
|
||||
session->state = SESSION_STATE_READY;
|
||||
|
||||
dcb_printf(session->client, "Welcome the SkySQL MaxScale Debug Interface (%s).\n",
|
||||
version_str);
|
||||
dcb_printf(session->client, "Type help for a list of available commands.\n\n");
|
||||
|
||||
return (void *)client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close a session with the router, this is the mechanism
|
||||
* by which a router may cleanup data structure etc.
|
||||
*
|
||||
* @param instance The router instance data
|
||||
* @param router_session The session being closed
|
||||
*/
|
||||
static void
|
||||
closeSession(ROUTER *instance, void *router_session)
|
||||
{
|
||||
CLI_INSTANCE *inst = (CLI_INSTANCE *)instance;
|
||||
CLI_SESSION *session = (CLI_SESSION *)router_session;
|
||||
|
||||
|
||||
spinlock_acquire(&inst->lock);
|
||||
if (inst->sessions == session)
|
||||
inst->sessions = session->next;
|
||||
else
|
||||
{
|
||||
CLI_SESSION *ptr = inst->sessions;
|
||||
while (ptr && ptr->next != session)
|
||||
ptr = ptr->next;
|
||||
if (ptr)
|
||||
ptr->next = session->next;
|
||||
}
|
||||
spinlock_release(&inst->lock);
|
||||
|
||||
/*
|
||||
* We are no longer in the linked list, free
|
||||
* all the memory and other resources associated
|
||||
* to the client session.
|
||||
*/
|
||||
free(session);
|
||||
}
|
||||
|
||||
/**
|
||||
* We have data from the client, we must route it to the backend.
|
||||
* This is simply a case of sending it to the connection that was
|
||||
* chosen when we started the client session.
|
||||
*
|
||||
* @param instance The router instance
|
||||
* @param router_session The router session returned from the newSession call
|
||||
* @param queue The queue of data buffers to route
|
||||
* @return The number of bytes sent
|
||||
*/
|
||||
static int
|
||||
execute(ROUTER *instance, void *router_session, GWBUF *queue)
|
||||
{
|
||||
CLI_SESSION *session = (CLI_SESSION *)router_session;
|
||||
|
||||
/* Extract the characters */
|
||||
while (queue)
|
||||
{
|
||||
strncat(session->cmdbuf, GWBUF_DATA(queue), GWBUF_LENGTH(queue));
|
||||
queue = gwbuf_consume(queue, GWBUF_LENGTH(queue));
|
||||
}
|
||||
|
||||
if (strrchr(session->cmdbuf, '\n'))
|
||||
{
|
||||
if (execute_cmd(session))
|
||||
dcb_printf(session->session->client, "MaxScale> ");
|
||||
else
|
||||
session->session->client->func.close(session->session->client);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display router diagnostics
|
||||
*
|
||||
* @param instance Instance of the router
|
||||
* @param dcb DCB to send diagnostics to
|
||||
*/
|
||||
static void
|
||||
diagnostics(ROUTER *instance, DCB *dcb)
|
||||
{
|
||||
return; /* Nothing to do currently */
|
||||
}
|
||||
572
server/modules/routing/debugcmd.c
Normal file
572
server/modules/routing/debugcmd.c
Normal file
@ -0,0 +1,572 @@
|
||||
/*
|
||||
* This file is distributed as part of the SkySQL Gateway. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright SkySQL Ab 2013
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file debugcmd.c - The debug CLI command line interpreter
|
||||
*
|
||||
* The command interpreter for the dbug user interface. The command
|
||||
* structure is such that there are a numerb of commands, notably
|
||||
* show and a set of subcommands, the things to show in this case.
|
||||
*
|
||||
* Each subcommand has a handler function defined for it that is passeed
|
||||
* the DCB to use to print the output of the commands and up to 3 arguments
|
||||
* as numeric values.
|
||||
*
|
||||
* There are two "built in" commands, the help command and the quit
|
||||
* command.
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 20/06/13 Mark Riddoch Initial implementation
|
||||
* 17/07/13 Mark Riddoch Additional commands
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <service.h>
|
||||
#include <session.h>
|
||||
#include <router.h>
|
||||
#include <modules.h>
|
||||
#include <atomic.h>
|
||||
#include <server.h>
|
||||
#include <spinlock.h>
|
||||
#include <dcb.h>
|
||||
#include <poll.h>
|
||||
#include <users.h>
|
||||
#include <dbusers.h>
|
||||
#include <config.h>
|
||||
#include <telnetd.h>
|
||||
#include <adminusers.h>
|
||||
#include <monitor.h>
|
||||
#include <debugcli.h>
|
||||
|
||||
#define MAXARGS 5
|
||||
|
||||
#define ARG_TYPE_ADDRESS 1
|
||||
#define ARG_TYPE_STRING 2
|
||||
/**
|
||||
* The subcommand structure
|
||||
*
|
||||
* These are the options that may be passed to a command
|
||||
*/
|
||||
struct subcommand {
|
||||
char *arg1;
|
||||
int n_args;
|
||||
void (*fn)();
|
||||
char *help;
|
||||
int arg_types[3];
|
||||
};
|
||||
|
||||
static void telnetdShowUsers(DCB *);
|
||||
/**
|
||||
* The subcommands of the show command
|
||||
*/
|
||||
struct subcommand showoptions[] = {
|
||||
{ "dcbs", 0, dprintAllDCBs, "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",
|
||||
{ARG_TYPE_ADDRESS, 0, 0} },
|
||||
{ "epoll", 0, dprintPollStats, "Show the poll statistics",
|
||||
{0, 0, 0} },
|
||||
{ "modules", 0, dprintAllModules, "Show all currently loaded modules",
|
||||
{0, 0, 0} },
|
||||
{ "monitors", 0, monitorShowAll, "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",
|
||||
{0, 0, 0} },
|
||||
{ "services", 0, dprintAllServices, "Show all configured services in MaxScale",
|
||||
{0, 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",
|
||||
{0, 0, 0} },
|
||||
{ "users", 0, telnetdShowUsers, "Show statistics and user names for the debug interface",
|
||||
{ARG_TYPE_ADDRESS, 0, 0} },
|
||||
{ NULL, 0, NULL, NULL,
|
||||
{0, 0, 0} }
|
||||
};
|
||||
|
||||
extern void shutdown_gateway();
|
||||
static void shutdown_service(DCB *dcb, SERVICE *service);
|
||||
static void shutdown_monitor(DCB *dcb, MONITOR *monitor);
|
||||
|
||||
/**
|
||||
* The subcommands of the shutdown command
|
||||
*/
|
||||
struct subcommand shutdownoptions[] = {
|
||||
{ "gateway", 0, shutdown_gateway, "Shutdown MaxScale",
|
||||
{0, 0, 0} },
|
||||
{ "maxscale", 0, shutdown_gateway, "Shutdown the MaxScale gateway",
|
||||
{0, 0, 0} },
|
||||
{ "monitor", 1, shutdown_monitor, "Shutdown a monitor, e.g. shutdown monitor 0x48381e0",
|
||||
{ARG_TYPE_ADDRESS, 0, 0} },
|
||||
{ "service", 1, shutdown_service, "Shutdown a service, e.g. shutdown service 0x4838320",
|
||||
{ARG_TYPE_ADDRESS, 0, 0} },
|
||||
{ NULL, 0, NULL, NULL,
|
||||
{0, 0, 0} }
|
||||
};
|
||||
|
||||
static void restart_service(DCB *dcb, SERVICE *service);
|
||||
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 0x4838320",
|
||||
{ARG_TYPE_ADDRESS, 0, 0} },
|
||||
{ NULL, 0, NULL, NULL,
|
||||
{0, 0, 0} }
|
||||
};
|
||||
|
||||
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,
|
||||
{0, 0, 0} }
|
||||
};
|
||||
|
||||
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,
|
||||
{0, 0, 0} }
|
||||
};
|
||||
|
||||
static void reload_users(DCB *dcb, SERVICE *service);
|
||||
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} },
|
||||
{ "users", 1, reload_users, "Reload the user data for a service. E.g. reload users 0x849420",
|
||||
{ARG_TYPE_ADDRESS, 0, 0} },
|
||||
{ NULL, 0, NULL, NULL,
|
||||
{0, 0, 0} }
|
||||
};
|
||||
|
||||
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",
|
||||
{ARG_TYPE_STRING, ARG_TYPE_STRING, 0} },
|
||||
{ NULL, 0, NULL, NULL,
|
||||
{0, 0, 0} }
|
||||
};
|
||||
|
||||
/**
|
||||
* The debug command table
|
||||
*/
|
||||
static struct {
|
||||
char *cmd;
|
||||
struct subcommand *options;
|
||||
} cmds[] = {
|
||||
{ "add", addoptions },
|
||||
{ "clear", clearoptions },
|
||||
{ "restart", restartoptions },
|
||||
{ "set", setoptions },
|
||||
{ "show", showoptions },
|
||||
{ "shutdown", shutdownoptions },
|
||||
{ "reload", reloadoptions },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Convert a string argument to a numeric, observing prefixes
|
||||
* for number bases, e.g. 0x for hex, 0 for octal
|
||||
*
|
||||
* @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)
|
||||
{
|
||||
switch (arg_type)
|
||||
{
|
||||
case ARG_TYPE_ADDRESS:
|
||||
return (unsigned long)strtol(arg, NULL, 0);
|
||||
case ARG_TYPE_STRING:
|
||||
return (unsigned long)arg;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* We have a complete line from the user, lookup the commands and execute them
|
||||
*
|
||||
* Commands are tokenised based on white space and then the firt
|
||||
* word is checked againts the cmds table. If a amtch is found the
|
||||
* second word is compared to the different options for that command.
|
||||
*
|
||||
* Commands may also take up to 3 additional arguments, these are all
|
||||
* assumed to the numeric values and will be converted before being passed
|
||||
* to the handler function for the command.
|
||||
*
|
||||
* @param cli The CLI_SESSION
|
||||
* @return Returns 0 if the interpreter should exit
|
||||
*/
|
||||
int
|
||||
execute_cmd(CLI_SESSION *cli)
|
||||
{
|
||||
DCB *dcb = cli->session->client;
|
||||
int argc, i, j, found = 0;
|
||||
char *args[MAXARGS];
|
||||
char *saveptr, *delim = " \t\r\n";
|
||||
unsigned long arg1, arg2, arg3;
|
||||
|
||||
/* Tokenize the input string */
|
||||
args[0] = strtok_r(cli->cmdbuf, delim, &saveptr);
|
||||
i = 0;
|
||||
do {
|
||||
i++;
|
||||
args[i] = strtok_r(NULL, delim, &saveptr);
|
||||
} while (args[i] != NULL && i < MAXARGS);
|
||||
|
||||
if (args[0] == NULL)
|
||||
return 1;
|
||||
argc = i - 2; /* The number of extra arguments to commands */
|
||||
|
||||
|
||||
if (!strcasecmp(args[0], "help"))
|
||||
{
|
||||
if (args[1] == NULL)
|
||||
{
|
||||
found = 1;
|
||||
dcb_printf(dcb, "Available commands:\n");
|
||||
for (i = 0; cmds[i].cmd; i++)
|
||||
{
|
||||
for (j = 0; cmds[i].options[j].arg1; j++)
|
||||
{
|
||||
dcb_printf(dcb, " %s %s\n", cmds[i].cmd, cmds[i].options[j].arg1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; cmds[i].cmd; i++)
|
||||
{
|
||||
if (!strcasecmp(args[1], cmds[i].cmd))
|
||||
{
|
||||
found = 1;
|
||||
dcb_printf(dcb, "Available options to the %s command:\n", args[1]);
|
||||
for (j = 0; cmds[i].options[j].arg1; j++)
|
||||
{
|
||||
dcb_printf(dcb, " %-10s %s\n", cmds[i].options[j].arg1,
|
||||
cmds[i].options[j].help);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found == 0)
|
||||
{
|
||||
dcb_printf(dcb, "No command %s to offer help with\n", args[1]);
|
||||
}
|
||||
}
|
||||
found = 1;
|
||||
}
|
||||
else if (!strcasecmp(args[0], "quit"))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if (argc >= 0)
|
||||
{
|
||||
for (i = 0; cmds[i].cmd; i++)
|
||||
{
|
||||
if (strcasecmp(args[0], cmds[i].cmd) == 0)
|
||||
{
|
||||
for (j = 0; cmds[i].options[j].arg1; j++)
|
||||
{
|
||||
if (strcasecmp(args[1], cmds[i].options[j].arg1) == 0)
|
||||
{
|
||||
if (argc != cmds[i].options[j].n_args)
|
||||
{
|
||||
dcb_printf(dcb, "Incorrect number of arguments: %s %s expects %d arguments\n",
|
||||
cmds[i].cmd, cmds[i].options[j].arg1,
|
||||
cmds[i].options[j].n_args);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (cmds[i].options[j].n_args)
|
||||
{
|
||||
case 0:
|
||||
cmds[i].options[j].fn(dcb);
|
||||
break;
|
||||
case 1:
|
||||
arg1 = convert_arg(args[2],cmds[i].options[j].arg_types[0]);
|
||||
if (arg1)
|
||||
cmds[i].options[j].fn(dcb, arg1);
|
||||
else
|
||||
dcb_printf(dcb, "Invalid argument: %s\n",
|
||||
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]);
|
||||
if (arg1 && arg2)
|
||||
cmds[i].options[j].fn(dcb, arg1, arg2);
|
||||
else if (arg1 == 0)
|
||||
dcb_printf(dcb, "Invalid argument: %s\n",
|
||||
args[2]);
|
||||
else
|
||||
dcb_printf(dcb, "Invalid argument: %s\n",
|
||||
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]);
|
||||
if (arg1 && arg2 && arg3)
|
||||
cmds[i].options[j].fn(dcb, arg1, arg2, arg3);
|
||||
else if (arg1 == 0)
|
||||
dcb_printf(dcb, "Invalid argument: %s\n",
|
||||
args[2]);
|
||||
else if (arg2 == 0)
|
||||
dcb_printf(dcb, "Invalid argument: %s\n",
|
||||
args[3]);
|
||||
else if (arg3 == 0)
|
||||
dcb_printf(dcb, "Invalid argument: %s\n",
|
||||
args[4]);
|
||||
}
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
dcb_printf(dcb,
|
||||
"Unknown option for the %s command. Valid sub-commands are:\n",
|
||||
cmds[i].cmd);
|
||||
for (j = 0; cmds[i].options[j].arg1; j++)
|
||||
{
|
||||
dcb_printf(dcb, " %-10s %s\n", cmds[i].options[j].arg1,
|
||||
cmds[i].options[j].help);
|
||||
}
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (argc == -1)
|
||||
{
|
||||
dcb_printf(dcb,
|
||||
"Commands must consist of at least two words. Type help for a list of commands\n");
|
||||
found = 1;
|
||||
}
|
||||
if (!found)
|
||||
dcb_printf(dcb,
|
||||
"Command '%s' not known, type help for a list of available commands\n", args[0]);
|
||||
memset(cli->cmdbuf, 0, 80);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug command to stop a service
|
||||
*
|
||||
* @param dcb The DCB to print any output to
|
||||
* @param service The service to shutdown
|
||||
*/
|
||||
static void
|
||||
shutdown_service(DCB *dcb, SERVICE *service)
|
||||
{
|
||||
serviceStop(service);
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug command to restart a stopped service
|
||||
*
|
||||
* @param dcb The DCB to print any output to
|
||||
* @param service The service to restart
|
||||
*/
|
||||
static void
|
||||
restart_service(DCB *dcb, SERVICE *service)
|
||||
{
|
||||
serviceRestart(service);
|
||||
}
|
||||
|
||||
static struct {
|
||||
char *str;
|
||||
unsigned int bit;
|
||||
} ServerBits[] = {
|
||||
{ "running", SERVER_RUNNING },
|
||||
{ "master", SERVER_MASTER },
|
||||
{ "slave", SERVER_SLAVE },
|
||||
{ "joined", SERVER_JOINED },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
/**
|
||||
* Map the server status bit
|
||||
*
|
||||
* @param str String representation
|
||||
* @return bit value or 0 on error
|
||||
*/
|
||||
static unsigned int
|
||||
server_map_status(char *str)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; ServerBits[i].str; i++)
|
||||
if (!strcasecmp(str, ServerBits[i].str))
|
||||
return ServerBits[i].bit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the status bit of a server
|
||||
*
|
||||
* @param dcb DCB to send output to
|
||||
* @param server The server to set the status of
|
||||
* @param bit String representation of the status bit
|
||||
*/
|
||||
static void
|
||||
set_server(DCB *dcb, SERVER *server, char *bit)
|
||||
{
|
||||
unsigned int bitvalue;
|
||||
|
||||
if ((bitvalue = server_map_status(bit)) != 0)
|
||||
server_set_status(server, bitvalue);
|
||||
else
|
||||
dcb_printf(dcb, "Unknown status bit %s\n", bit);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clear the status bit of a server
|
||||
*
|
||||
* @param dcb DCB to send output to
|
||||
* @param server The server to set the status of
|
||||
* @param bit String representation of the status bit
|
||||
*/
|
||||
static void
|
||||
clear_server(DCB *dcb, SERVER *server, char *bit)
|
||||
{
|
||||
unsigned int bitvalue;
|
||||
|
||||
if ((bitvalue = server_map_status(bit)) != 0)
|
||||
server_clear_status(server, bitvalue);
|
||||
else
|
||||
dcb_printf(dcb, "Unknown status bit %s\n", bit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload the authenticaton data from the backend database of a service.
|
||||
*
|
||||
* @param dcb DCB to send output
|
||||
* @param service The service to update
|
||||
*/
|
||||
static void
|
||||
reload_users(DCB *dcb, SERVICE *service)
|
||||
{
|
||||
dcb_printf(dcb, "Loaded %d database users for server %s.\n",
|
||||
reload_mysql_users(service), service->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Relaod the configuration data from the config file
|
||||
*
|
||||
* @param dcb DCB to use to send output
|
||||
*/
|
||||
static void
|
||||
reload_config(DCB *dcb)
|
||||
{
|
||||
dcb_printf(dcb, "Reloading configuration from file.\n");
|
||||
config_reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new admin user
|
||||
*
|
||||
* @param dcb The DCB for messages
|
||||
* @param user The user name
|
||||
* @param passwd The Password of the user
|
||||
*/
|
||||
static void
|
||||
telnetdAddUser(DCB *dcb, char *user, char *passwd)
|
||||
{
|
||||
char *err;
|
||||
|
||||
if (admin_test_user(user))
|
||||
{
|
||||
dcb_printf(dcb, "User %s already exists.\n", user);
|
||||
return;
|
||||
}
|
||||
if ((err = admin_add_user(user, passwd)) == NULL)
|
||||
dcb_printf(dcb, "User %s has been successfully added.\n", user);
|
||||
else
|
||||
dcb_printf(dcb, "Failed to add new user. %s\n", err);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the adminsitration users
|
||||
*
|
||||
* @param dcb The DCB to print the user data to
|
||||
*/
|
||||
static void
|
||||
telnetdShowUsers(DCB *dcb)
|
||||
{
|
||||
dcb_printf(dcb, "Administration interface users:\n");
|
||||
dcb_PrintAdminUsers(dcb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Command to shutdown a running monitor
|
||||
*
|
||||
* @param dcb The DCB to use to print messages
|
||||
* @param monitor The monitor to shutdown
|
||||
*/
|
||||
static void
|
||||
shutdown_monitor(DCB *dcb, MONITOR *monitor)
|
||||
{
|
||||
monitorStop(monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Command to restart a stopped monitor
|
||||
*
|
||||
* @param dcb The DCB to use to print messages
|
||||
* @param monitor The monitor to restart
|
||||
*/
|
||||
static void
|
||||
restart_monitor(DCB *dcb, MONITOR *monitor)
|
||||
{
|
||||
monitorStart(monitor);
|
||||
}
|
||||
128
server/modules/routing/depend.mk
Normal file
128
server/modules/routing/depend.mk
Normal file
@ -0,0 +1,128 @@
|
||||
testroute.o: testroute.c /usr/include/stdio.h /usr/include/features.h \
|
||||
/usr/include/sys/cdefs.h /usr/include/bits/wordsize.h \
|
||||
/usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stddef.h \
|
||||
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
|
||||
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdarg.h \
|
||||
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
|
||||
../../include/router.h ../../include/service.h /usr/include/time.h \
|
||||
/usr/include/bits/time.h /usr/include/xlocale.h ../../include/spinlock.h \
|
||||
../../include/thread.h /usr/include/pthread.h /usr/include/endian.h \
|
||||
/usr/include/bits/endian.h /usr/include/bits/byteswap.h \
|
||||
/usr/include/sched.h /usr/include/bits/sched.h \
|
||||
/usr/include/bits/pthreadtypes.h /usr/include/bits/setjmp.h \
|
||||
../../include/dcb.h ../../include/buffer.h ../../include/gwbitmask.h \
|
||||
../../include/server.h ../../include/session.h
|
||||
readconnroute.o: readconnroute.c /usr/include/stdio.h \
|
||||
/usr/include/features.h /usr/include/sys/cdefs.h \
|
||||
/usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
|
||||
/usr/include/gnu/stubs-64.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stddef.h \
|
||||
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
|
||||
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdarg.h \
|
||||
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
|
||||
/usr/include/stdlib.h /usr/include/bits/waitflags.h \
|
||||
/usr/include/bits/waitstatus.h /usr/include/endian.h \
|
||||
/usr/include/bits/endian.h /usr/include/bits/byteswap.h \
|
||||
/usr/include/sys/types.h /usr/include/time.h /usr/include/sys/select.h \
|
||||
/usr/include/bits/select.h /usr/include/bits/sigset.h \
|
||||
/usr/include/bits/time.h /usr/include/sys/sysmacros.h \
|
||||
/usr/include/bits/pthreadtypes.h /usr/include/alloca.h \
|
||||
/usr/include/string.h /usr/include/xlocale.h ../../include/service.h \
|
||||
../../include/spinlock.h ../../include/thread.h /usr/include/pthread.h \
|
||||
/usr/include/sched.h /usr/include/bits/sched.h \
|
||||
/usr/include/bits/setjmp.h ../../include/dcb.h ../../include/buffer.h \
|
||||
../../include/gwbitmask.h ../../include/server.h ../../include/router.h \
|
||||
../../include/session.h ../../include/atomic.h \
|
||||
../include/readconnection.h \
|
||||
/home/mriddoch/Repository/skygateway/utils/skygw_types.h \
|
||||
/usr/include/math.h /usr/include/bits/huge_val.h \
|
||||
/usr/include/bits/huge_valf.h /usr/include/bits/huge_vall.h \
|
||||
/usr/include/bits/inf.h /usr/include/bits/nan.h \
|
||||
/usr/include/bits/mathdef.h /usr/include/bits/mathcalls.h \
|
||||
/home/mriddoch/Repository/skygateway/utils/skygw_utils.h \
|
||||
/home/mriddoch/Repository/skygateway/utils/skygw_types.h \
|
||||
/home/mriddoch/Repository/skygateway/utils/skygw_debug.h \
|
||||
/usr/include/assert.h /usr/include/unistd.h \
|
||||
/usr/include/bits/posix_opt.h /usr/include/bits/environments.h \
|
||||
/usr/include/bits/confname.h /usr/include/getopt.h \
|
||||
/home/mriddoch/Repository/skygateway/log_manager/log_manager.h \
|
||||
../include/mysql_client_server_protocol.h /usr/include/stdint.h \
|
||||
/usr/include/bits/wchar.h /usr/include/openssl/sha.h \
|
||||
/usr/include/openssl/e_os2.h /usr/include/openssl/opensslconf.h \
|
||||
/usr/include/openssl/opensslconf-x86_64.h /usr/include/sys/ioctl.h \
|
||||
/usr/include/bits/ioctls.h /usr/include/asm/ioctls.h \
|
||||
/usr/include/asm-generic/ioctls.h /usr/include/linux/ioctl.h \
|
||||
/usr/include/asm/ioctl.h /usr/include/asm-generic/ioctl.h \
|
||||
/usr/include/bits/ioctl-types.h /usr/include/sys/ttydefaults.h \
|
||||
/usr/include/errno.h /usr/include/bits/errno.h \
|
||||
/usr/include/linux/errno.h /usr/include/asm/errno.h \
|
||||
/usr/include/asm-generic/errno.h /usr/include/asm-generic/errno-base.h \
|
||||
/usr/include/sys/socket.h /usr/include/sys/uio.h /usr/include/bits/uio.h \
|
||||
/usr/include/bits/socket.h /usr/include/bits/sockaddr.h \
|
||||
/usr/include/asm/socket.h /usr/include/asm-generic/socket.h \
|
||||
/usr/include/asm/sockios.h /usr/include/asm-generic/sockios.h \
|
||||
/usr/include/netinet/in.h /usr/include/bits/in.h \
|
||||
/usr/include/arpa/inet.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdbool.h \
|
||||
/usr/include/fcntl.h /usr/include/bits/fcntl.h /usr/include/bits/stat.h \
|
||||
../../include/poll.h ../../include/users.h ../../include/hashtable.h
|
||||
debugcli.o: debugcli.c /usr/include/stdio.h /usr/include/features.h \
|
||||
/usr/include/sys/cdefs.h /usr/include/bits/wordsize.h \
|
||||
/usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stddef.h \
|
||||
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
|
||||
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdarg.h \
|
||||
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
|
||||
/usr/include/stdlib.h /usr/include/bits/waitflags.h \
|
||||
/usr/include/bits/waitstatus.h /usr/include/endian.h \
|
||||
/usr/include/bits/endian.h /usr/include/bits/byteswap.h \
|
||||
/usr/include/sys/types.h /usr/include/time.h /usr/include/sys/select.h \
|
||||
/usr/include/bits/select.h /usr/include/bits/sigset.h \
|
||||
/usr/include/bits/time.h /usr/include/sys/sysmacros.h \
|
||||
/usr/include/bits/pthreadtypes.h /usr/include/alloca.h \
|
||||
/usr/include/string.h /usr/include/xlocale.h ../../include/service.h \
|
||||
../../include/spinlock.h ../../include/thread.h /usr/include/pthread.h \
|
||||
/usr/include/sched.h /usr/include/bits/sched.h \
|
||||
/usr/include/bits/setjmp.h ../../include/dcb.h ../../include/buffer.h \
|
||||
../../include/gwbitmask.h ../../include/server.h ../../include/session.h \
|
||||
../../include/router.h ../../include/modules.h ../../include/atomic.h \
|
||||
../../include/poll.h ../include/debugcli.h \
|
||||
/home/mriddoch/Repository/skygateway/utils/skygw_utils.h \
|
||||
/home/mriddoch/Repository/skygateway/utils/skygw_types.h \
|
||||
/usr/include/math.h /usr/include/bits/huge_val.h \
|
||||
/usr/include/bits/huge_valf.h /usr/include/bits/huge_vall.h \
|
||||
/usr/include/bits/inf.h /usr/include/bits/nan.h \
|
||||
/usr/include/bits/mathdef.h /usr/include/bits/mathcalls.h \
|
||||
/home/mriddoch/Repository/skygateway/utils/skygw_debug.h \
|
||||
/usr/include/assert.h /usr/include/unistd.h \
|
||||
/usr/include/bits/posix_opt.h /usr/include/bits/environments.h \
|
||||
/usr/include/bits/confname.h /usr/include/getopt.h \
|
||||
/home/mriddoch/Repository/skygateway/log_manager/log_manager.h
|
||||
debugcmd.o: debugcmd.c /usr/include/stdio.h /usr/include/features.h \
|
||||
/usr/include/sys/cdefs.h /usr/include/bits/wordsize.h \
|
||||
/usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stddef.h \
|
||||
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
|
||||
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdarg.h \
|
||||
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
|
||||
/usr/include/stdlib.h /usr/include/bits/waitflags.h \
|
||||
/usr/include/bits/waitstatus.h /usr/include/endian.h \
|
||||
/usr/include/bits/endian.h /usr/include/bits/byteswap.h \
|
||||
/usr/include/sys/types.h /usr/include/time.h /usr/include/sys/select.h \
|
||||
/usr/include/bits/select.h /usr/include/bits/sigset.h \
|
||||
/usr/include/bits/time.h /usr/include/sys/sysmacros.h \
|
||||
/usr/include/bits/pthreadtypes.h /usr/include/alloca.h \
|
||||
/usr/include/string.h /usr/include/xlocale.h ../../include/service.h \
|
||||
../../include/spinlock.h ../../include/thread.h /usr/include/pthread.h \
|
||||
/usr/include/sched.h /usr/include/bits/sched.h \
|
||||
/usr/include/bits/setjmp.h ../../include/dcb.h ../../include/buffer.h \
|
||||
../../include/gwbitmask.h ../../include/server.h ../../include/session.h \
|
||||
../../include/router.h ../../include/modules.h ../../include/atomic.h \
|
||||
../../include/poll.h ../../include/users.h ../../include/hashtable.h \
|
||||
../../include/dbusers.h ../../include/config.h ../include/telnetd.h \
|
||||
../../include/adminusers.h ../include/debugcli.h
|
||||
452
server/modules/routing/readconnroute.c
Normal file
452
server/modules/routing/readconnroute.c
Normal file
@ -0,0 +1,452 @@
|
||||
/*
|
||||
* This file is distributed as part of the SkySQL Gateway. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright SkySQL Ab 2013
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file readconnroute.c - Read Connection Load Balancing Query Router
|
||||
*
|
||||
* This is the implementation of a simple query router that balances
|
||||
* read connections. It assumes the service is configured with a set
|
||||
* of slaves and that the application clients already split read and write
|
||||
* queries. It offers a service to balance the client read connections
|
||||
* over this set of slave servers. It does this once only, at the time
|
||||
* the connection is made. It chooses the server that currently has the least
|
||||
* number of connections by keeping a count for each server of how
|
||||
* many connections the query router has made to the server.
|
||||
*
|
||||
* When two servers have the same number of current connections the one with
|
||||
* the least number of connections since startup will be used.
|
||||
*
|
||||
* The router may also have options associated to it that will limit the
|
||||
* choice of backend server. Currently two options are supported, the "master"
|
||||
* option will cause the router to only connect to servers marked as masters
|
||||
* and the "slave" option will limit connections to routers that are marked
|
||||
* as slaves. If neither option is specified the router will connect to either
|
||||
* masters or slaves.
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 14/06/2013 Mark Riddoch Initial implementation
|
||||
* 25/06/2013 Mark Riddoch Addition of checks for current server state
|
||||
* 26/06/2013 Mark Riddoch Use server with least connections since
|
||||
* startup if the number of current
|
||||
* connections is the same for two servers
|
||||
* Addition of master and slave options
|
||||
* 27/06/2013 Vilho Raatikka Added skygw_log_write command as an example
|
||||
* and necessary headers.
|
||||
* 17/07/2013 Massimiliano Pinto Added clientReply routine:
|
||||
* called by backend server to send data to client
|
||||
* Included mysql_client_server_protocol.h
|
||||
* with macros and MySQL commands with MYSQL_ prefix
|
||||
* avoiding any conflict with the standard ones
|
||||
* in mysql.h
|
||||
* 22/07/13 Mark Riddoch Addition of joined router option for Galera
|
||||
* clusters
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <service.h>
|
||||
#include <server.h>
|
||||
#include <router.h>
|
||||
#include <atomic.h>
|
||||
#include <spinlock.h>
|
||||
#include <readconnection.h>
|
||||
#include <dcb.h>
|
||||
#include <spinlock.h>
|
||||
|
||||
#include <skygw_types.h>
|
||||
#include <skygw_utils.h>
|
||||
#include <log_manager.h>
|
||||
|
||||
#include <mysql_client_server_protocol.h>
|
||||
|
||||
static char *version_str = "V1.0.2";
|
||||
|
||||
/* The router entry points */
|
||||
static ROUTER *createInstance(SERVICE *service, char **options);
|
||||
static void *newSession(ROUTER *instance, SESSION *session);
|
||||
static void closeSession(ROUTER *instance, void *router_session);
|
||||
static int routeQuery(ROUTER *instance, void *router_session, GWBUF *queue);
|
||||
static void diagnostics(ROUTER *instance, DCB *dcb);
|
||||
static void clientReply(ROUTER* instance, void* router_session, GWBUF* queue, DCB *backend_dcb);
|
||||
|
||||
/** The module object definition */
|
||||
static ROUTER_OBJECT MyObject = { createInstance, newSession, closeSession, routeQuery, diagnostics, clientReply };
|
||||
|
||||
static SPINLOCK instlock;
|
||||
static INSTANCE *instances;
|
||||
|
||||
/**
|
||||
* Implementation of the mandatory version entry point
|
||||
*
|
||||
* @return version string of the module
|
||||
*/
|
||||
char *
|
||||
version()
|
||||
{
|
||||
return version_str;
|
||||
}
|
||||
|
||||
/**
|
||||
* The module initialisation routine, called when the module
|
||||
* is first loaded.
|
||||
*/
|
||||
void
|
||||
ModuleInit()
|
||||
{
|
||||
skygw_log_write(NULL,
|
||||
LOGFILE_MESSAGE,
|
||||
"Initialise readconnroute router module %s.\n", version_str);
|
||||
spinlock_init(&instlock);
|
||||
instances = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* The module entry point routine. It is this routine that
|
||||
* must populate the structure that is referred to as the
|
||||
* "module object", this is a structure with the set of
|
||||
* external entry points for this module.
|
||||
*
|
||||
* @return The module object
|
||||
*/
|
||||
ROUTER_OBJECT *
|
||||
GetModuleObject()
|
||||
{
|
||||
return &MyObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the router for a particular service
|
||||
* within the gateway.
|
||||
*
|
||||
* @param service The service this router is being create for
|
||||
* @param options An array of options for this query router
|
||||
*
|
||||
* @return The instance data for this new instance
|
||||
*/
|
||||
static ROUTER *
|
||||
createInstance(SERVICE *service, char **options)
|
||||
{
|
||||
INSTANCE *inst;
|
||||
SERVER *server;
|
||||
int i, n;
|
||||
|
||||
if ((inst = malloc(sizeof(INSTANCE))) == NULL)
|
||||
return NULL;
|
||||
|
||||
memset(&inst->stats, 0, sizeof(ROUTER_STATS));
|
||||
|
||||
inst->service = service;
|
||||
spinlock_init(&inst->lock);
|
||||
inst->connections = NULL;
|
||||
|
||||
/*
|
||||
* We need an array of the backend servers in the instance structure so
|
||||
* that we can maintain a count of the number of connections to each
|
||||
* backend server.
|
||||
*/
|
||||
for (server = service->databases, n = 0; server; server = server->nextdb)
|
||||
n++;
|
||||
|
||||
inst->servers = (BACKEND **)calloc(n + 1, sizeof(BACKEND *));
|
||||
if (!inst->servers)
|
||||
{
|
||||
free(inst);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (server = service->databases, n = 0; server; server = server->nextdb)
|
||||
{
|
||||
if ((inst->servers[n] = malloc(sizeof(BACKEND))) == NULL)
|
||||
{
|
||||
for (i = 0; i < n; i++)
|
||||
free(inst->servers[i]);
|
||||
free(inst->servers);
|
||||
free(inst);
|
||||
return NULL;
|
||||
}
|
||||
inst->servers[n]->server = server;
|
||||
inst->servers[n]->count = 0;
|
||||
n++;
|
||||
}
|
||||
inst->servers[n] = NULL;
|
||||
|
||||
/*
|
||||
* Process the options
|
||||
*/
|
||||
inst->bitmask = 0;
|
||||
inst->bitvalue = 0;
|
||||
if (options)
|
||||
{
|
||||
for (i = 0; options[i]; i++)
|
||||
{
|
||||
if (!strcasecmp(options[i], "master"))
|
||||
{
|
||||
inst->bitmask |= (SERVER_MASTER|SERVER_SLAVE);
|
||||
inst->bitvalue |= SERVER_MASTER;
|
||||
}
|
||||
else if (!strcasecmp(options[i], "slave"))
|
||||
{
|
||||
inst->bitmask |= (SERVER_MASTER|SERVER_SLAVE);
|
||||
inst->bitvalue |= SERVER_SLAVE;
|
||||
}
|
||||
else if (!strcasecmp(options[i], "joined"))
|
||||
{
|
||||
inst->bitmask |= (SERVER_JOINED);
|
||||
inst->bitvalue |= SERVER_JOINED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We have completed the creation of the instance data, so now
|
||||
* insert this router instance into the linked list of routers
|
||||
* that have been created with this module.
|
||||
*/
|
||||
spinlock_acquire(&instlock);
|
||||
inst->next = instances;
|
||||
instances = inst;
|
||||
spinlock_release(&instlock);
|
||||
|
||||
return (ROUTER *)inst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate a new session with this instance of the router.
|
||||
*
|
||||
* @param instance The router instance data
|
||||
* @param session The session itself
|
||||
* @return Session specific data for this session
|
||||
*/
|
||||
static void *
|
||||
newSession(ROUTER *instance, SESSION *session)
|
||||
{
|
||||
INSTANCE *inst = (INSTANCE *)instance;
|
||||
CLIENT_SESSION *client;
|
||||
BACKEND *candidate = NULL;
|
||||
int i;
|
||||
|
||||
if ((client = (CLIENT_SESSION *)malloc(sizeof(CLIENT_SESSION))) == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
/*
|
||||
* Find a backend server to connect to. This is the extent of the
|
||||
* load balancing algorithm we need to implement for this simple
|
||||
* connection router.
|
||||
*/
|
||||
|
||||
/* First find a running server to set as our initial candidate server */
|
||||
for (i = 0; inst->servers[i]; i++)
|
||||
{
|
||||
if (inst->servers[i] && SERVER_IS_RUNNING(inst->servers[i]->server)
|
||||
&& (inst->servers[i]->server->status & inst->bitmask) == inst->bitvalue)
|
||||
{
|
||||
candidate = inst->servers[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Loop over all the servers and find any that have fewer connections than our
|
||||
* candidate server.
|
||||
*
|
||||
* If a server has less connections than the current candidate we mark this
|
||||
* as the new candidate to connect to.
|
||||
*
|
||||
* If a server has the same number of connections currently as the candidate
|
||||
* and has had less connections over time than the candidate it will also
|
||||
* become the new candidate. This has the effect of spreading the connections
|
||||
* over different servers during periods of very low load.
|
||||
*/
|
||||
for (i = 1; inst->servers[i]; i++)
|
||||
{
|
||||
if (inst->servers[i] && SERVER_IS_RUNNING(inst->servers[i]->server)
|
||||
&& (inst->servers[i]->server->status & inst->bitmask) == inst->bitvalue)
|
||||
{
|
||||
if (inst->servers[i]->count < candidate->count)
|
||||
{
|
||||
candidate = inst->servers[i];
|
||||
}
|
||||
else if (inst->servers[i]->count == candidate->count &&
|
||||
inst->servers[i]->server->stats.n_connections
|
||||
< candidate->server->stats.n_connections)
|
||||
{
|
||||
candidate = inst->servers[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We now have the server with the least connections.
|
||||
* Bump the connection count for this server
|
||||
*/
|
||||
atomic_add(&candidate->count, 1);
|
||||
|
||||
client->backend = candidate;
|
||||
|
||||
/*
|
||||
* Open a backend connection, putting the DCB for this
|
||||
* connection in the client->dcb
|
||||
*/
|
||||
|
||||
if ((client->dcb = dcb_connect(candidate->server, session,
|
||||
candidate->server->protocol)) == NULL)
|
||||
{
|
||||
atomic_add(&candidate->count, -1);
|
||||
free(client);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inst->stats.n_sessions++;
|
||||
|
||||
/* Add this session to the list of active sessions */
|
||||
spinlock_acquire(&inst->lock);
|
||||
client->next = inst->connections;
|
||||
inst->connections = client;
|
||||
spinlock_release(&inst->lock);
|
||||
return (void *)client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close a session with the router, this is the mechanism
|
||||
* by which a router may cleanup data structure etc.
|
||||
*
|
||||
* @param instance The router instance data
|
||||
* @param router_session The session being closed
|
||||
*/
|
||||
static void
|
||||
closeSession(ROUTER *instance, void *router_session)
|
||||
{
|
||||
INSTANCE *inst = (INSTANCE *)instance;
|
||||
CLIENT_SESSION *session = (CLIENT_SESSION *)router_session;
|
||||
|
||||
/*
|
||||
* Close the connection to the backend
|
||||
*/
|
||||
session->dcb->func.close(session->dcb);
|
||||
atomic_add(&session->backend->count, -1);
|
||||
atomic_add(&session->backend->server->stats.n_current, -1);
|
||||
|
||||
spinlock_acquire(&inst->lock);
|
||||
if (inst->connections == session)
|
||||
inst->connections = session->next;
|
||||
else
|
||||
{
|
||||
CLIENT_SESSION *ptr = inst->connections;
|
||||
while (ptr && ptr->next != session)
|
||||
ptr = ptr->next;
|
||||
if (ptr)
|
||||
ptr->next = session->next;
|
||||
}
|
||||
spinlock_release(&inst->lock);
|
||||
|
||||
/*
|
||||
* We are no longer in the linked list, free
|
||||
* all the memory and other resources associated
|
||||
* to the client session.
|
||||
*/
|
||||
free(session);
|
||||
}
|
||||
|
||||
/**
|
||||
* We have data from the client, we must route it to the backend.
|
||||
* This is simply a case of sending it to the connection that was
|
||||
* chosen when we started the client session.
|
||||
*
|
||||
* @param instance The router instance
|
||||
* @param router_session The router session returned from the newSession call
|
||||
* @param queue The queue of data buffers to route
|
||||
* @return The number of bytes sent
|
||||
*/
|
||||
static int
|
||||
routeQuery(ROUTER *instance, void *router_session, GWBUF *queue)
|
||||
{
|
||||
INSTANCE *inst = (INSTANCE *)instance;
|
||||
CLIENT_SESSION *session = (CLIENT_SESSION *)router_session;
|
||||
uint8_t *payload = GWBUF_DATA(queue);
|
||||
int mysql_command = -1;
|
||||
|
||||
inst->stats.n_queries++;
|
||||
|
||||
mysql_command = MYSQL_GET_COMMAND(payload);
|
||||
|
||||
switch(mysql_command) {
|
||||
case MYSQL_COM_CHANGE_USER:
|
||||
return session->dcb->func.auth(session->dcb, NULL, session->dcb->session, queue);
|
||||
default:
|
||||
return session->dcb->func.write(session->dcb, queue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display router diagnostics
|
||||
*
|
||||
* @param instance Instance of the router
|
||||
* @param dcb DCB to send diagnostics to
|
||||
*/
|
||||
static void
|
||||
diagnostics(ROUTER *instance, DCB *dcb)
|
||||
{
|
||||
INSTANCE *inst = (INSTANCE *)instance;
|
||||
CLIENT_SESSION *session;
|
||||
int i = 0;
|
||||
|
||||
spinlock_acquire(&inst->lock);
|
||||
session = inst->connections;
|
||||
while (session)
|
||||
{
|
||||
i++;
|
||||
session = session->next;
|
||||
}
|
||||
spinlock_release(&inst->lock);
|
||||
|
||||
dcb_printf(dcb, "\tNumber of router sessions: %d\n", inst->stats.n_sessions);
|
||||
dcb_printf(dcb, "\tCurrent no. of router sessions: %d\n", i);
|
||||
dcb_printf(dcb, "\tNumber of queries forwarded: %d\n", inst->stats.n_queries);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
INSTANCE* inst = NULL;
|
||||
DCB *client = NULL;
|
||||
CLIENT_SESSION* session = NULL;
|
||||
|
||||
inst = (INSTANCE *)instance;
|
||||
session = (CLIENT_SESSION *)router_session;
|
||||
|
||||
client = backend_dcb->session->client;
|
||||
|
||||
client->func.write(client, queue);
|
||||
}
|
||||
///
|
||||
65
server/modules/routing/readwritesplit/Makefile
Normal file
65
server/modules/routing/readwritesplit/Makefile
Normal file
@ -0,0 +1,65 @@
|
||||
# This file is distributed as part of the SkySQL Gateway. It is free
|
||||
# software: you can redistribute it and/or modify it under the terms of the
|
||||
# GNU General Public License as published by the Free Software Foundation,
|
||||
# version 2.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# Copyright SkySQL Ab 2013
|
||||
#
|
||||
# Revision History
|
||||
# Date Who Description
|
||||
# 27/06/13 Mark Riddoch Initial framework put in place
|
||||
|
||||
include ../../../../build_gateway.inc
|
||||
|
||||
LOGPATH := $(ROOT_PATH)/log_manager
|
||||
UTILSPATH := $(ROOT_PATH)/utils
|
||||
QCLASSPATH := $(ROOT_PATH)/query_classifier
|
||||
|
||||
CC=cc
|
||||
CFLAGS=-c -fPIC -I/usr/include -I../../include -I../../../include \
|
||||
-I$(LOGPATH) -I$(UTILSPATH) -I$(QCLASSPATH) \
|
||||
-I$(MARIADB_SRC_PATH)/include -Wall -g
|
||||
|
||||
include ../../../../makefile.inc
|
||||
|
||||
LDFLAGS=-shared -L$(LOGPATH) -L$(QCLASSPATH) -L$(MARIADB_SRC_PATH)/libmysqld \
|
||||
-Wl,-rpath,$(DEST)/lib \
|
||||
-Wl,-rpath,$(LOGPATH) -Wl,-rpath,$(UTILSPATH) -Wl,-rpath,$(QCLASSPATH) \
|
||||
-Wl,-rpath,$(MARIADB_SRC_PATH)/libmysqld
|
||||
|
||||
SRCS=readwritesplit.c
|
||||
OBJ=$(SRCS:.c=.o)
|
||||
LIBS=-lssl -pthread -llog_manager -lquery_classifier -lmysqld
|
||||
MODULES=libreadwritesplit.so
|
||||
|
||||
all: $(MODULES)
|
||||
|
||||
libreadwritesplit.so: $(OBJ)
|
||||
$(CC) $(LDFLAGS) $(OBJ) $(UTILSPATH)/skygw_utils.o $(LIBS) -o $@
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(OBJ) $(MODULES)
|
||||
|
||||
tags:
|
||||
ctags $(SRCS) $(HDRS)
|
||||
|
||||
depend:
|
||||
@rm -f depend.mk
|
||||
cc -M $(CFLAGS) $(SRCS) > depend.mk
|
||||
|
||||
install: $(MODULES)
|
||||
install -D $(MODULES) $(DEST)/MaxScale/modules
|
||||
|
||||
include depend.mk
|
||||
43
server/modules/routing/readwritesplit/depend.mk
Normal file
43
server/modules/routing/readwritesplit/depend.mk
Normal file
@ -0,0 +1,43 @@
|
||||
readwritesplit.o: readwritesplit.c /usr/include/stdio.h \
|
||||
/usr/include/features.h /usr/include/sys/cdefs.h \
|
||||
/usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
|
||||
/usr/include/gnu/stubs-64.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stddef.h \
|
||||
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
|
||||
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdarg.h \
|
||||
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
|
||||
/usr/include/strings.h /usr/include/xlocale.h /usr/include/string.h \
|
||||
../../../include/router.h ../../../include/service.h /usr/include/time.h \
|
||||
/usr/include/bits/time.h ../../../include/spinlock.h \
|
||||
../../../include/thread.h /usr/include/pthread.h /usr/include/endian.h \
|
||||
/usr/include/bits/endian.h /usr/include/bits/byteswap.h \
|
||||
/usr/include/sched.h /usr/include/bits/sched.h \
|
||||
/usr/include/bits/pthreadtypes.h /usr/include/bits/setjmp.h \
|
||||
../../../include/dcb.h ../../../include/buffer.h \
|
||||
../../../include/gwbitmask.h ../../../include/server.h \
|
||||
../../../include/session.h ../../include/readwritesplit.h \
|
||||
/usr/include/stdlib.h /usr/include/bits/waitflags.h \
|
||||
/usr/include/bits/waitstatus.h /usr/include/sys/types.h \
|
||||
/usr/include/sys/select.h /usr/include/bits/select.h \
|
||||
/usr/include/bits/sigset.h /usr/include/sys/sysmacros.h \
|
||||
/usr/include/alloca.h /packages/mariadb-5.5.25/include/mysql.h \
|
||||
/packages/mariadb-5.5.25/include/mysql_version.h \
|
||||
/packages/mariadb-5.5.25/include/mysql_com.h \
|
||||
/packages/mariadb-5.5.25/include/mysql_time.h \
|
||||
/packages/mariadb-5.5.25/include/my_list.h \
|
||||
/packages/mariadb-5.5.25/include/typelib.h \
|
||||
/packages/mariadb-5.5.25/include/my_alloc.h \
|
||||
/home/mriddoch/Repository/skygateway/utils/skygw_utils.h \
|
||||
/home/mriddoch/Repository/skygateway/utils/skygw_types.h \
|
||||
/usr/include/math.h /usr/include/bits/huge_val.h \
|
||||
/usr/include/bits/huge_valf.h /usr/include/bits/huge_vall.h \
|
||||
/usr/include/bits/inf.h /usr/include/bits/nan.h \
|
||||
/usr/include/bits/mathdef.h /usr/include/bits/mathcalls.h \
|
||||
/home/mriddoch/Repository/skygateway/utils/skygw_debug.h \
|
||||
/usr/include/assert.h /usr/include/unistd.h \
|
||||
/usr/include/bits/posix_opt.h /usr/include/bits/environments.h \
|
||||
/usr/include/bits/confname.h /usr/include/getopt.h \
|
||||
/home/mriddoch/Repository/skygateway/log_manager/log_manager.h \
|
||||
/home/mriddoch/Repository/skygateway/query_classifier/query_classifier.h \
|
||||
/home/mriddoch/Repository/skygateway/query_classifier/../utils/skygw_utils.h
|
||||
585
server/modules/routing/readwritesplit/readwritesplit.c
Normal file
585
server/modules/routing/readwritesplit/readwritesplit.c
Normal file
@ -0,0 +1,585 @@
|
||||
/*
|
||||
* This file is distributed as part of the SkySQL Gateway. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright SkySQL Ab 2013
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <strings.h>
|
||||
#include <string.h>
|
||||
#include <router.h>
|
||||
#include <readwritesplit.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <mysql.h>
|
||||
#include <skygw_utils.h>
|
||||
#include <log_manager.h>
|
||||
#include <query_classifier.h>
|
||||
#include <dcb.h>
|
||||
#include <spinlock.h>
|
||||
|
||||
/**
|
||||
* @file readwritesplit.c The entry points for the read/write query splitting
|
||||
* router module.
|
||||
*
|
||||
* This file contains the entry points that comprise the API to the read write
|
||||
* query splitting router.
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 01/07/2013 Vilho Raatikka Initial implementation
|
||||
* 15/07/2013 Massimiliano Pinto Added clientReply
|
||||
* from master only in case of session change
|
||||
* 17/07/2013 Massimiliano Pinto clientReply is now used by mysql_backend
|
||||
* for all reply situations
|
||||
* 18/07/2013 Massimiliano Pinto routeQuery now handles COM_QUIT
|
||||
* as QUERY_TYPE_SESSION_WRITE
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
static char *version_str = "V1.0.2";
|
||||
|
||||
static ROUTER* createInstance(SERVICE *service, char **options);
|
||||
static void* newSession(ROUTER *instance, SESSION *session);
|
||||
static void closeSession(ROUTER *instance, void *session);
|
||||
static int routeQuery(ROUTER *instance, void *session, GWBUF *queue);
|
||||
static void diagnostic(ROUTER *instance, DCB *dcb);
|
||||
static void clientReply(ROUTER* instance, void* router_session, GWBUF* queue, DCB *backend_dcb);
|
||||
|
||||
static ROUTER_OBJECT MyObject =
|
||||
{ createInstance,
|
||||
newSession,
|
||||
closeSession,
|
||||
routeQuery,
|
||||
diagnostic,
|
||||
clientReply };
|
||||
|
||||
static SPINLOCK instlock;
|
||||
static INSTANCE* instances;
|
||||
|
||||
/**
|
||||
* Implementation of the mandatory version entry point
|
||||
*
|
||||
* @return version string of the module
|
||||
*/
|
||||
char *
|
||||
version()
|
||||
{
|
||||
return version_str;
|
||||
}
|
||||
|
||||
/**
|
||||
* The module initialisation routine, called when the module
|
||||
* is first loaded.
|
||||
*/
|
||||
void
|
||||
ModuleInit()
|
||||
{
|
||||
skygw_log_write_flush(NULL,
|
||||
LOGFILE_MESSAGE,
|
||||
"Initialize read/write split router module.\n");
|
||||
spinlock_init(&instlock);
|
||||
instances = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* The module entry point routine. It is this routine that
|
||||
* must populate the structure that is referred to as the
|
||||
* "module object", this is a structure with the set of
|
||||
* external entry points for this module.
|
||||
*
|
||||
* @return The module object
|
||||
*/
|
||||
ROUTER_OBJECT* GetModuleObject() {
|
||||
skygw_log_write(NULL,
|
||||
LOGFILE_TRACE,
|
||||
"Returning readwritesplit router module object.");
|
||||
return &MyObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the router for a particular service
|
||||
* within the gateway.
|
||||
*
|
||||
* The job of ths entry point is to create the service wide data needed
|
||||
* for the query router. This is information needed to route queries that
|
||||
* is not related to any individual client session, exmaples of data that
|
||||
* might be stored in the ROUTER object for a particular query router are
|
||||
* connections counts, last used connection etc so that balancing may
|
||||
* take place.
|
||||
*
|
||||
* @param service The service this router is being create for
|
||||
* @param options The options for this query router
|
||||
*
|
||||
* @return The instance data for this new instance
|
||||
*/
|
||||
static ROUTER* createInstance(
|
||||
SERVICE* service,
|
||||
char** options)
|
||||
{
|
||||
INSTANCE* inst;
|
||||
SERVER* server;
|
||||
int n;
|
||||
int i;
|
||||
|
||||
if ((inst = calloc(1, sizeof(INSTANCE))) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
inst->service = service;
|
||||
spinlock_init(&inst->lock);
|
||||
inst->connections = NULL;
|
||||
|
||||
/** Calculate number of servers */
|
||||
for (server = service->databases, n = 0; server; server = server->nextdb) {
|
||||
n++;
|
||||
}
|
||||
inst->servers = (BACKEND **)calloc(n + 1, sizeof(BACKEND *));
|
||||
|
||||
if (!inst->servers) {
|
||||
free(inst);
|
||||
return NULL;
|
||||
}
|
||||
/**
|
||||
* We need an array of the backend servers in the instance structure so
|
||||
* that we can maintain a count of the number of connections to each
|
||||
* backend server.
|
||||
*/
|
||||
for (server = service->databases, n = 0; server; server = server->nextdb) {
|
||||
|
||||
if ((inst->servers[n] = malloc(sizeof(BACKEND))) == NULL) {
|
||||
for (i = 0; i < n; i++) {
|
||||
free(inst->servers[i]);
|
||||
}
|
||||
free(inst->servers);
|
||||
free(inst);
|
||||
return NULL;
|
||||
}
|
||||
inst->servers[n]->server = server;
|
||||
inst->servers[n]->count = 0;
|
||||
n++;
|
||||
}
|
||||
inst->servers[n] = NULL;
|
||||
|
||||
/**
|
||||
* We have completed the creation of the instance data, so now
|
||||
* insert this router instance into the linked list of routers
|
||||
* that have been created with this module.
|
||||
*/
|
||||
spinlock_acquire(&instlock);
|
||||
inst->next = instances;
|
||||
instances = inst;
|
||||
spinlock_release(&instlock);
|
||||
|
||||
return (ROUTER *)inst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate a new session with this instance of the router.
|
||||
*
|
||||
* The session is used to store all the data required for a particular
|
||||
* client connection.
|
||||
*
|
||||
* @param instance The router instance data
|
||||
* @param session The session itself
|
||||
* @return Session specific data for this session
|
||||
*/
|
||||
static void* newSession(
|
||||
ROUTER* instance,
|
||||
SESSION* session)
|
||||
{
|
||||
BACKEND* candidate = NULL;
|
||||
CLIENT_SESSION* client;
|
||||
INSTANCE* inst = (INSTANCE *)instance;
|
||||
int i;
|
||||
|
||||
if ((client = (CLIENT_SESSION *)malloc(sizeof(CLIENT_SESSION))) == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a backend server to connect to. This is the extent of the
|
||||
* load balancing algorithm we need to implement for this simple
|
||||
* connection router.
|
||||
*/
|
||||
for (i = 0; inst->servers[i]; i++)
|
||||
{
|
||||
|
||||
if (inst->servers[i] && SERVER_IS_SLAVE(inst->servers[i]->server))
|
||||
{
|
||||
candidate = inst->servers[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loop over all the servers and find any that have fewer connections than our
|
||||
* candidate server.
|
||||
*
|
||||
* If a server has less connections than the current candidate we mark this
|
||||
* as the new candidate to connect to.
|
||||
*
|
||||
* If a server has the same number of connections currently as the candidate
|
||||
* and has had less connections over time than the candidate it will also
|
||||
* become the new candidate. This has the effect of spreading the connections
|
||||
* over different servers during periods of very low load.
|
||||
*/
|
||||
for (i = 0; inst->servers[i]; i++) {
|
||||
|
||||
if (inst->servers[i]
|
||||
&& SERVER_IS_RUNNING(inst->servers[i]->server))
|
||||
{
|
||||
if (SERVER_IS_SLAVE(inst->servers[i]->server))
|
||||
{
|
||||
if (inst->servers[i]->count < candidate->count) {
|
||||
candidate = inst->servers[i];
|
||||
} else if (inst->servers[i]->count == candidate->count &&
|
||||
inst->servers[i]->server->stats.n_connections
|
||||
< candidate->server->stats.n_connections)
|
||||
{
|
||||
candidate = inst->servers[i];
|
||||
}
|
||||
} else if (SERVER_IS_MASTER(inst->servers[i]->server)) {
|
||||
/** master is found */
|
||||
inst->master = inst->servers[i];
|
||||
}
|
||||
}
|
||||
} /* for */
|
||||
|
||||
if (inst->master == NULL) {
|
||||
inst->master = inst->servers[i-1];
|
||||
}
|
||||
/**
|
||||
* We now have a master and a slave server with the least connections.
|
||||
* Bump the connection counts for these servers.
|
||||
*/
|
||||
atomic_add(&candidate->count, 1);
|
||||
client->slave = candidate;
|
||||
atomic_add(&inst->master->count, 1);
|
||||
client->master = inst->master;
|
||||
ss_dassert(client->master->server != candidate->server);
|
||||
|
||||
/**
|
||||
* Open the slave connection.
|
||||
*/
|
||||
if ((client->slaveconn = dcb_connect(candidate->server, session,
|
||||
candidate->server->protocol)) == NULL)
|
||||
{
|
||||
atomic_add(&candidate->count, -1);
|
||||
free(client);
|
||||
return NULL;
|
||||
}
|
||||
/**
|
||||
* Open the master connection.
|
||||
*/
|
||||
if ((client->masterconn = dcb_connect(client->master->server, session,
|
||||
client->master->server->protocol)) == NULL)
|
||||
{
|
||||
atomic_add(&client->master->count, -1);
|
||||
free(client);
|
||||
return NULL;
|
||||
}
|
||||
inst->stats.n_sessions += 1;
|
||||
|
||||
/* Add this session to end of the list of active sessions */
|
||||
spinlock_acquire(&inst->lock);
|
||||
client->next = inst->connections;
|
||||
inst->connections = client;
|
||||
spinlock_release(&inst->lock);
|
||||
|
||||
return (void *)client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close a session with the router, this is the mechanism
|
||||
* by which a router may cleanup data structure etc.
|
||||
*
|
||||
* @param instance The router instance data
|
||||
* @param session The session being closed
|
||||
*/
|
||||
static void closeSession(
|
||||
ROUTER* instance,
|
||||
void* router_session)
|
||||
{
|
||||
INSTANCE* inst = (INSTANCE *)instance;
|
||||
CLIENT_SESSION* session = (CLIENT_SESSION *)router_session;
|
||||
|
||||
/**
|
||||
* Close the connection to the backend servers
|
||||
*/
|
||||
session->slaveconn->func.close(session->slaveconn);
|
||||
session->masterconn->func.close(session->masterconn);
|
||||
atomic_add(&session->slave->count, -1);
|
||||
atomic_add(&session->master->count, -1);
|
||||
atomic_add(&session->slave->server->stats.n_current, -1);
|
||||
atomic_add(&session->master->server->stats.n_current, -1);
|
||||
|
||||
spinlock_acquire(&inst->lock);
|
||||
if (inst->connections == session) {
|
||||
inst->connections = session->next;
|
||||
} else {
|
||||
CLIENT_SESSION* ptr = inst->connections;
|
||||
|
||||
while (ptr && ptr->next != session) {
|
||||
ptr = ptr->next;
|
||||
}
|
||||
|
||||
if (ptr) {
|
||||
ptr->next = session->next;
|
||||
}
|
||||
}
|
||||
spinlock_release(&inst->lock);
|
||||
|
||||
/*
|
||||
* We are no longer in the linked list, free
|
||||
* all the memory and other resources associated
|
||||
* to the client session.
|
||||
*/
|
||||
free(session);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The main routing entry, this is called with every packet that is
|
||||
* received and has to be forwarded to the backend database.
|
||||
*
|
||||
* The routeQuery will make the routing decision based on the contents
|
||||
* of the instance, session and the query itself in the queue. The
|
||||
* data in the queue may not represent a complete query, it represents
|
||||
* the data that has been received. The query router itself is responsible
|
||||
* for buffering the partial query, a later call to the query router will
|
||||
* contain the remainder, or part thereof of the query.
|
||||
*
|
||||
* @param instance The query router instance
|
||||
* @param session The session associated with the client
|
||||
* @param queue Gateway buffer queue with the packets received
|
||||
*
|
||||
* @return The number of queries forwarded
|
||||
*/
|
||||
static int routeQuery(
|
||||
ROUTER* instance,
|
||||
void* router_session,
|
||||
GWBUF* queue)
|
||||
{
|
||||
skygw_query_type_t qtype = QUERY_TYPE_UNKNOWN;
|
||||
char* querystr = NULL;
|
||||
char* startpos;
|
||||
size_t len;
|
||||
unsigned char packet_type, *packet;
|
||||
int ret = 0;
|
||||
GWBUF *cq = NULL;
|
||||
|
||||
INSTANCE* inst = (INSTANCE *)instance;
|
||||
CLIENT_SESSION* session = (CLIENT_SESSION *)router_session;
|
||||
inst->stats.n_queries++;
|
||||
|
||||
packet = GWBUF_DATA(queue);
|
||||
packet_type = packet[4];
|
||||
startpos = (char *)&packet[5];
|
||||
len = packet[0];
|
||||
len += 255*packet[1];
|
||||
len += 255*255*packet[2];
|
||||
|
||||
switch(packet_type) {
|
||||
case COM_QUIT: /**< 1 QUIT will close all sessions */
|
||||
case COM_INIT_DB: /**< 2 DDL must go to the master */
|
||||
case COM_REFRESH: /**< 7 - I guess this is session but not sure */
|
||||
case COM_DEBUG: /**< 0d all servers dump debug info to stdout */
|
||||
case COM_PING: /**< 0e all servers are pinged */
|
||||
case COM_CHANGE_USER: /**< 11 all servers change it accordingly */
|
||||
qtype = QUERY_TYPE_SESSION_WRITE;
|
||||
break;
|
||||
|
||||
case COM_CREATE_DB: /**< 5 DDL must go to the master */
|
||||
case COM_DROP_DB: /**< 6 DDL must go to the master */
|
||||
qtype = QUERY_TYPE_WRITE;
|
||||
break;
|
||||
|
||||
case COM_QUERY:
|
||||
querystr = (char *)malloc(len);
|
||||
memcpy(querystr, startpos, len-1);
|
||||
memset(&querystr[len-1], 0, 1);
|
||||
qtype = skygw_query_classifier_get_type(querystr, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
case COM_SHUTDOWN: /**< 8 where should shutdown be routed ? */
|
||||
case COM_STATISTICS: /**< 9 ? */
|
||||
case COM_PROCESS_INFO: /**< 0a ? */
|
||||
case COM_CONNECT: /**< 0b ? */
|
||||
case COM_PROCESS_KILL: /**< 0c ? */
|
||||
case COM_TIME: /**< 0f should this be run in gateway ? */
|
||||
case COM_DELAYED_INSERT: /**< 10 ? */
|
||||
case COM_DAEMON: /**< 1d ? */
|
||||
break;
|
||||
}
|
||||
|
||||
#if defined(SS_DEBUG_)
|
||||
skygw_log_write(NULL, LOGFILE_TRACE, "String\t\"%s\"", querystr);
|
||||
skygw_log_write(NULL,
|
||||
LOGFILE_TRACE,
|
||||
"Packet type\t%s",
|
||||
STRPACKETTYPE(packet_type));
|
||||
#endif
|
||||
|
||||
switch (qtype) {
|
||||
case QUERY_TYPE_WRITE:
|
||||
#if defined(SS_DEBUG_)
|
||||
skygw_log_write(NULL,
|
||||
LOGFILE_TRACE,
|
||||
"Query type\t%s, routing to Master.",
|
||||
STRQTYPE(qtype));
|
||||
#endif
|
||||
ret = session->masterconn->func.write(session->masterconn, queue);
|
||||
atomic_add(&inst->stats.n_master, 1);
|
||||
goto return_ret;
|
||||
break;
|
||||
|
||||
case QUERY_TYPE_READ:
|
||||
#if defined(SS_DEBUG_)
|
||||
skygw_log_write(NULL,
|
||||
LOGFILE_TRACE,
|
||||
"Query type\t%s, routing to Slave.",
|
||||
STRQTYPE(qtype));
|
||||
#endif
|
||||
ret = session->slaveconn->func.write(session->slaveconn, queue);
|
||||
atomic_add(&inst->stats.n_slave, 1);
|
||||
goto return_ret;
|
||||
break;
|
||||
|
||||
|
||||
case QUERY_TYPE_SESSION_WRITE:
|
||||
#if defined(SS_DEBUG_)
|
||||
skygw_log_write(NULL,
|
||||
LOGFILE_TRACE,
|
||||
"Query type\t%s, routing to All servers.",
|
||||
STRQTYPE(qtype));
|
||||
#endif
|
||||
/**
|
||||
* TODO! Connection to all servers must be established, and
|
||||
* the command must be executed in them.
|
||||
*/
|
||||
|
||||
cq = gwbuf_clone(queue);
|
||||
|
||||
switch(packet_type) {
|
||||
case COM_QUIT:
|
||||
ret = session->masterconn->func.write(session->masterconn, queue);
|
||||
session->slaveconn->func.write(session->slaveconn, cq);
|
||||
break;
|
||||
case COM_CHANGE_USER:
|
||||
session->masterconn->func.auth(session->masterconn, NULL, session->masterconn->session, queue);
|
||||
session->slaveconn->func.auth(session->slaveconn, NULL, session->masterconn->session, cq);
|
||||
break;
|
||||
default:
|
||||
ret = session->masterconn->func.session(session->masterconn, (void *)queue);
|
||||
session->slaveconn->func.session(session->slaveconn, (void *)cq);
|
||||
break;
|
||||
}
|
||||
|
||||
atomic_add(&inst->stats.n_all, 1);
|
||||
goto return_ret;
|
||||
break;
|
||||
|
||||
default:
|
||||
#if defined(SS_DEBUG_)
|
||||
skygw_log_write(NULL,
|
||||
LOGFILE_TRACE,
|
||||
"Query type\t%s, routing to Master by default.",
|
||||
STRQTYPE(qtype));
|
||||
#endif
|
||||
/** Is this really ok? */
|
||||
ret = session->masterconn->func.write(session->masterconn, queue);
|
||||
atomic_add(&inst->stats.n_master, 1);
|
||||
goto return_ret;
|
||||
break;
|
||||
}
|
||||
|
||||
return_ret:
|
||||
free(querystr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Diagnostics routine
|
||||
*
|
||||
* Print query router statistics to the DCB passed in
|
||||
*
|
||||
* @param instance The router instance
|
||||
* @param dcb The DCB for diagnostic output
|
||||
*/
|
||||
static void
|
||||
diagnostic(ROUTER *instance, DCB *dcb)
|
||||
{
|
||||
CLIENT_SESSION *session;
|
||||
INSTANCE *inst = (INSTANCE *)instance;
|
||||
int i = 0;
|
||||
|
||||
spinlock_acquire(&inst->lock);
|
||||
session = inst->connections;
|
||||
while (session)
|
||||
{
|
||||
i++;
|
||||
session = session->next;
|
||||
}
|
||||
spinlock_release(&inst->lock);
|
||||
|
||||
dcb_printf(dcb, "\tNumber of router sessions: %d\n", inst->stats.n_sessions);
|
||||
dcb_printf(dcb, "\tCurrent no. of router sessions: %d\n", i);
|
||||
dcb_printf(dcb, "\tNumber of queries forwarded: %d\n", inst->stats.n_queries);
|
||||
dcb_printf(dcb, "\tNumber of queries forwarded to master: %d\n", inst->stats.n_master);
|
||||
dcb_printf(dcb, "\tNumber of queries forwarded to slave: %d\n", inst->stats.n_slave);
|
||||
dcb_printf(dcb, "\tNumber of queries forwarded to all: %d\n", inst->stats.n_all);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Client Reply routine
|
||||
*
|
||||
* The routine will reply to client for session change with master server data
|
||||
*
|
||||
* @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)
|
||||
{
|
||||
INSTANCE* inst = NULL;
|
||||
DCB *master = NULL;
|
||||
DCB *client = NULL;
|
||||
CLIENT_SESSION* session = NULL;
|
||||
|
||||
inst = (INSTANCE *)instance;
|
||||
session = (CLIENT_SESSION *)router_session;
|
||||
master = session->masterconn;
|
||||
client = backend_dcb->session->client;
|
||||
|
||||
if (backend_dcb->command == ROUTER_CHANGE_SESSION) {
|
||||
/* if backend_dcb is the master we can reply to the client */
|
||||
if (backend_dcb == master) {
|
||||
master->session->client->func.write(master->session->client, queue);
|
||||
} else {
|
||||
/* just consume the gwbuf without writing to the client */
|
||||
gwbuf_consume(queue, gwbuf_length(queue));
|
||||
}
|
||||
} else {
|
||||
/* normal flow */
|
||||
client->func.write(client, queue);
|
||||
}
|
||||
}
|
||||
///
|
||||
123
server/modules/routing/testroute.c
Normal file
123
server/modules/routing/testroute.c
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* This file is distributed as part of the SkySQL Gateway. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright SkySQL Ab 2013
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <router.h>
|
||||
|
||||
static char *version_str = "V1.0.0";
|
||||
|
||||
static ROUTER *createInstance(SERVICE *service, char **options);
|
||||
static void *newSession(ROUTER *instance, SESSION *session);
|
||||
static void closeSession(ROUTER *instance, void *session);
|
||||
static int routeQuery(ROUTER *instance, void *session, GWBUF *queue);
|
||||
static void diagnostic(ROUTER *instance, DCB *dcb);
|
||||
|
||||
static ROUTER_OBJECT MyObject = { createInstance, newSession, closeSession, routeQuery, diagnostic, NULL };
|
||||
|
||||
/**
|
||||
* Implementation of the mandatory version entry point
|
||||
*
|
||||
* @return version string of the module
|
||||
*/
|
||||
char *
|
||||
version()
|
||||
{
|
||||
return version_str;
|
||||
}
|
||||
|
||||
/**
|
||||
* The module initialisation routine, called when the module
|
||||
* is first loaded.
|
||||
*/
|
||||
void
|
||||
ModuleInit()
|
||||
{
|
||||
fprintf(stderr, "Initial test router module.\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* The module entry point routine. It is this routine that
|
||||
* must populate the structure that is referred to as the
|
||||
* "module object", this is a structure with the set of
|
||||
* external entry points for this module.
|
||||
*
|
||||
* @return The module object
|
||||
*/
|
||||
ROUTER_OBJECT *
|
||||
GetModuleObject()
|
||||
{
|
||||
fprintf(stderr, "Returing test router module object.\n");
|
||||
return &MyObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the router for a particular service
|
||||
* within the gateway.
|
||||
*
|
||||
* @param service The service this router is being create for
|
||||
* @param options The options for this query router
|
||||
*
|
||||
* @return The instance data for this new instance
|
||||
*/
|
||||
static ROUTER *
|
||||
createInstance(SERVICE *service, char **options)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate a new session with this instance of the router.
|
||||
*
|
||||
* @param instance The router instance data
|
||||
* @param session The session itself
|
||||
* @return Session specific data for this session
|
||||
*/
|
||||
static void *
|
||||
newSession(ROUTER *instance, SESSION *session)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close a session with the router, this is the mechanism
|
||||
* by which a router may cleanup data structure etc.
|
||||
*
|
||||
* @param instance The router instance data
|
||||
* @param session The session being closed
|
||||
*/
|
||||
static void
|
||||
closeSession(ROUTER *instance, void *session)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
routeQuery(ROUTER *instance, void *session, GWBUF *queue)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Diagnostics routine
|
||||
*
|
||||
* @param instance The router instance
|
||||
* @param dcb The DCB for diagnostic output
|
||||
*/
|
||||
static void
|
||||
diagnostic(ROUTER *instance, DCB *dcb)
|
||||
{
|
||||
}
|
||||
Reference in New Issue
Block a user