1223 lines
36 KiB
C
1223 lines
36 KiB
C
//////////////////////////////////
|
|
// SKYSQL GATEWAY main module
|
|
// By Massimiliano Pinto 2012
|
|
// SkySQL AB
|
|
//////////////////////////////////
|
|
//
|
|
//////////////////////////////////
|
|
// //
|
|
// S K Y S Q L G A T E W A Y //
|
|
// //
|
|
//////////////////////////////////
|
|
|
|
#include "skysql_gw.h"
|
|
|
|
unsigned int mysql_errno(MYSQL_conn *mysql) {
|
|
return 1146U;
|
|
}
|
|
|
|
|
|
const char *mysql_sqlstate(MYSQL_conn *mysql) {
|
|
return "00000";
|
|
|
|
}
|
|
|
|
const char *mysql_error(MYSQL_conn *mysql) {
|
|
return "error 1111";
|
|
}
|
|
|
|
static char *strend(register const char *s)
|
|
{
|
|
while (*s++);
|
|
return (char*) (s-1);
|
|
}
|
|
|
|
int mysql_select_db(MYSQL_conn *conn, const char *db) {
|
|
apr_status_t rv;
|
|
//uint8_t *packet_buffer = NULL;
|
|
long bytes;
|
|
int ret = 1;
|
|
uint8_t packet_buffer[SMALL_CHUNK] = "";
|
|
|
|
// set COMM_INIT_DB
|
|
packet_buffer[4]= '\x02';
|
|
strncpy(packet_buffer+4+1, db, SMALL_CHUNK - 1);
|
|
|
|
//COMM_INIT_DB + DBNAME = paylod
|
|
skysql_set_byte3(packet_buffer, 1 + strlen(packet_buffer+4+1));
|
|
|
|
//packet header + payload = bytes to send
|
|
bytes = 4 + 1 + strlen(packet_buffer+4+1);
|
|
|
|
// send to server
|
|
rv = apr_socket_send(conn->socket, packet_buffer, &bytes);
|
|
|
|
if (rv != APR_SUCCESS) {
|
|
fprintf(stderr, "Errore in send query\n");
|
|
fflush(stderr);
|
|
return 1;
|
|
}
|
|
|
|
// now read the response from server
|
|
bytes = SMALL_CHUNK;
|
|
|
|
memset(&packet_buffer, '\0', sizeof(packet_buffer));
|
|
|
|
rv = apr_socket_recv(conn->socket, packet_buffer, &bytes);
|
|
ret = packet_buffer[4];
|
|
|
|
if (rv != APR_SUCCESS) {
|
|
fprintf(stderr, "Errore in recv\n");
|
|
fflush(stderr);
|
|
return 1;
|
|
}
|
|
|
|
if (ret == '\x00')
|
|
return 0;
|
|
else
|
|
return ret;
|
|
}
|
|
|
|
///////////////////////////////////////
|
|
// MYSQL_conn structure setup
|
|
// A new standalone pool is allocated
|
|
///////////////////////////////////////
|
|
static MYSQL_conn *mysql_init(MYSQL_conn *input) {
|
|
apr_pool_t *pool = NULL;
|
|
apr_status_t rv = -1;
|
|
|
|
if (input == NULL) {
|
|
// structure alloction
|
|
input = calloc(1, sizeof(MYSQL_conn));
|
|
if (input == NULL)
|
|
return NULL;
|
|
// new pool created
|
|
rv = apr_pool_create_core(&pool);
|
|
|
|
if (rv != APR_SUCCESS) {
|
|
|
|
#ifdef MYSQL_CONN_DEBUG
|
|
fprintf(stderr, "MYSQL_INIT: apr_pool_create_core FAILED\n";
|
|
fflush(stderr);
|
|
#endif
|
|
free(input);
|
|
return NULL;
|
|
}
|
|
|
|
// the structure now has the pool
|
|
input->pool = pool;
|
|
}
|
|
|
|
return input;
|
|
}
|
|
|
|
/////////////////////////////////////
|
|
// Send COM_QUIT to server
|
|
// Close socket
|
|
// free the pool
|
|
// free main pointer
|
|
/////////////////////////////////////
|
|
static void mysql_close(MYSQL_conn *conn) {
|
|
apr_status_t rv;
|
|
uint8_t packet_buffer[5];
|
|
long bytes = 5;
|
|
|
|
// Packet # is 0
|
|
packet_buffer[3]= '\x00';
|
|
|
|
// COM_QUIT is \x01
|
|
packet_buffer[4]= '\x01';
|
|
|
|
// set packet length to 1
|
|
skysql_set_byte3(packet_buffer, 1);
|
|
|
|
// send COM_QUIT
|
|
rv = apr_socket_send(conn->socket, packet_buffer, &bytes);
|
|
|
|
// close socket
|
|
apr_socket_close(conn->socket);
|
|
|
|
// pool destroy
|
|
apr_pool_destroy(conn->pool);
|
|
|
|
#ifdef MYSQL_CONN_DEBUG
|
|
fprintf(stderr, "Open/Close Connection %lu to backend closed/cleaned\n", conn->tid);
|
|
fflush(stderr);
|
|
#endif
|
|
// free structure pointer
|
|
if (conn)
|
|
free(conn);
|
|
}
|
|
|
|
int mysql_query(MYSQL_conn *conn, const char *query) {
|
|
apr_status_t rv;
|
|
//uint8_t *packet_buffer=NULL;
|
|
uint8_t packet_buffer[SMALL_CHUNK];
|
|
long bytes;
|
|
int fd;
|
|
|
|
//packet_buffer = (uint8_t *) calloc(1, 5 + strlen(query) + 1);
|
|
memset(&packet_buffer, '\0', sizeof(packet_buffer));
|
|
|
|
packet_buffer[4]= '\x03';
|
|
strcpy(packet_buffer+5, query);
|
|
|
|
skysql_set_byte3(packet_buffer, 1 + strlen(query));
|
|
|
|
bytes = 4 + 1 + strlen(query);
|
|
|
|
fprintf(stderr, "THE QUERY is [%s] len %i\n", query, bytes);
|
|
fprintf(stderr, "THE QUERY TID is [%lu]", conn->tid);
|
|
fprintf(stderr, "THE QUERY scramble is [%s]", conn->scramble);
|
|
if (conn->socket == NULL) {
|
|
fprintf(stderr, "***** THE QUERY sock struct is NULL\n");
|
|
}
|
|
fwrite(packet_buffer, bytes, 1, stderr);
|
|
fflush(stderr);
|
|
|
|
apr_os_sock_get(&fd,conn->socket);
|
|
|
|
fprintf(stderr, "QUERY Socket FD is %i\n", fd);
|
|
|
|
fflush(stderr);
|
|
|
|
rv = apr_socket_send(conn->socket, packet_buffer, &bytes);
|
|
|
|
fprintf(stderr, "QUERY SENT [%s]\n", query);
|
|
fflush(stderr);
|
|
|
|
if (rv != APR_SUCCESS) {
|
|
fprintf(stderr, "Errore in send query\n");
|
|
fflush(stderr);
|
|
return 1;
|
|
}
|
|
|
|
fprintf(stderr, "Query [%s] sent\n", query);
|
|
fflush(stderr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int mysql_print_result(MYSQL_conn *conn) {
|
|
apr_status_t rv;
|
|
uint8_t buffer[MAX_CHUNK];
|
|
long bytes;
|
|
|
|
bytes = 1024 * 16;
|
|
|
|
memset(buffer, '\0', sizeof(buffer));
|
|
|
|
rv = apr_socket_recv(conn->socket, buffer, &bytes);
|
|
|
|
if (rv != APR_SUCCESS) {
|
|
fprintf(stderr, "Errore in recv\n");
|
|
fflush(stderr);
|
|
return 1;
|
|
}
|
|
|
|
//fprintf(stderr, "Result with %li columns\n", buffer[4]);
|
|
//fwrite(buffer, bytes, 1, stderr);
|
|
//fflush(stderr);
|
|
|
|
return (int) buffer[4];
|
|
}
|
|
|
|
static int mysql_connect(char *host, int port, char *dbname, char *user, char *passwd, MYSQL_conn *conn) {
|
|
apr_status_t rv;
|
|
int connect = 0;
|
|
int ciclo = 0;
|
|
char buffer[SMALL_CHUNK];
|
|
uint8_t packet_buffer[SMALL_CHUNK];
|
|
char errmesg[128];
|
|
uint8_t *payload = NULL;
|
|
int server_protocol;
|
|
char server_version[100]="";
|
|
uint8_t *server_version_end = NULL;
|
|
uint16_t skysql_server_capabilities_one;
|
|
uint16_t skysql_server_capabilities_two;
|
|
int fd;
|
|
unsigned long tid =0;
|
|
apr_sockaddr_t *connessione;
|
|
apr_socket_t *socket = NULL;
|
|
long bytes;
|
|
uint8_t scramble_data_1[8 + 1] = "";
|
|
uint8_t scramble_data_2[12 + 1] = "";
|
|
uint8_t scramble_data[20 + 1] = "";
|
|
uint8_t capab_ptr[4];
|
|
int scramble_len;
|
|
uint8_t scramble[20 + 1];
|
|
uint8_t client_scramble[20 + 1];
|
|
uint8_t client_capabilities[4];
|
|
uint32_t server_capabilities;
|
|
uint32_t final_capabilities;
|
|
char dbpass[500]="";
|
|
apr_pool_t *pool = NULL;
|
|
|
|
pool = conn->pool;
|
|
|
|
apr_sockaddr_info_get(&connessione, host, APR_UNSPEC, port, 0, pool);
|
|
|
|
if ((rv = apr_socket_create(&socket, connessione->family, SOCK_STREAM, APR_PROTO_TCP, pool)) != APR_SUCCESS) {
|
|
fprintf(stderr, "Errore creazione socket: [%s] %i\n", strerror(errno), errno);
|
|
exit;
|
|
}
|
|
|
|
fprintf(stderr, "Socket initialized\n");
|
|
fflush(stderr);
|
|
|
|
conn->socket=socket;
|
|
|
|
rv = apr_socket_opt_set(socket, APR_TCP_NODELAY, 1);
|
|
rv = apr_socket_opt_set(socket, APR_SO_NONBLOCK , 0);
|
|
|
|
//apr_socket_timeout_set(socket, 355000);
|
|
|
|
if ((rv = apr_socket_connect(socket, connessione)) != APR_SUCCESS) {
|
|
apr_strerror(rv, errmesg, sizeof(errmesg));
|
|
fprintf(stderr, "Errore connect %i, %s: RV = [%i], [%s]\n", errno, strerror(errno), rv, errmesg);
|
|
apr_socket_close(socket);
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
connect = 1;
|
|
}
|
|
|
|
fprintf(stderr, "CONNECT is DONE\n");
|
|
|
|
apr_os_sock_get(&fd,socket);
|
|
|
|
fprintf(stderr, "Socket FD is %i\n", fd);
|
|
fflush(stderr);
|
|
|
|
memset(&buffer, '\0', sizeof(buffer));
|
|
|
|
bytes = 16384;
|
|
|
|
|
|
rv = apr_socket_recv(socket, buffer, &bytes);
|
|
|
|
if ( rv == APR_SUCCESS) {
|
|
fprintf(stderr, "RESPONSE ciclo %i HO letto [%s] bytes %li\n",ciclo, buffer, bytes);
|
|
fflush(stderr);
|
|
ciclo++;
|
|
} else {
|
|
if (APR_STATUS_IS_EOF(rv)) {
|
|
fprintf(stderr, "EOF reached. Bytes = %li\n", bytes);
|
|
} else {
|
|
apr_strerror(rv, errmesg, sizeof(errmesg));
|
|
fprintf(stderr, "###### Receive error FINAL : connection not completed %i %s: RV = [%i], [%s]\n", errno, strerror(errno), rv, errmesg);
|
|
|
|
apr_socket_close(socket);
|
|
|
|
exit;
|
|
}
|
|
}
|
|
|
|
fwrite(buffer, bytes, 1, stderr);
|
|
fflush(stderr);
|
|
|
|
//decode mysql handshake
|
|
|
|
payload = buffer + 4;
|
|
server_protocol= payload[0];
|
|
fprintf(stderr, "Server Protocol [%i]\n", server_protocol);
|
|
payload++;
|
|
fprintf(stderr, "Protocol Version [%s]\n", payload);
|
|
fflush(stderr);
|
|
|
|
server_version_end = strend((char*) payload);
|
|
payload = server_version_end + 1;
|
|
|
|
// TID
|
|
tid = skysql_get_byte4(payload);
|
|
memcpy(&conn->tid, &tid, 4);
|
|
fprintf(stderr, "Thread ID is %lu\n", conn->tid);
|
|
fflush(stderr);
|
|
|
|
payload +=4;
|
|
|
|
// scramble_part 1
|
|
memcpy(scramble_data_1, payload, 8);
|
|
payload += 8;
|
|
|
|
// 1 filler
|
|
payload++;
|
|
|
|
skysql_server_capabilities_one = skysql_get_byte2(payload);
|
|
fprintf(stderr, "Capab_1[\n");
|
|
fwrite(&skysql_server_capabilities_one, 2, 1, stderr);
|
|
fflush(stderr);
|
|
|
|
//2 capab_part 1 + 1 language + 2 server_status
|
|
payload +=5;
|
|
|
|
skysql_server_capabilities_two = skysql_get_byte2(payload);
|
|
fprintf(stderr, "]Capab_2[\n");
|
|
fwrite(&skysql_server_capabilities_two, 2, 1, stderr);
|
|
fprintf(stderr, "]\n");
|
|
fflush(stderr);
|
|
|
|
memcpy(&capab_ptr, &skysql_server_capabilities_one, 2);
|
|
fprintf(stderr, "Capab_1[\n");
|
|
fwrite(capab_ptr, 2, 1, stderr);
|
|
fflush(stderr);
|
|
|
|
memcpy(&(capab_ptr[2]), &skysql_server_capabilities_two, 2);
|
|
fprintf(stderr, "Capab_2[\n");
|
|
fwrite(capab_ptr, 2, 1, stderr);
|
|
fflush(stderr);
|
|
|
|
// 2 capab_part 2
|
|
payload+=2;
|
|
|
|
scramble_len = payload[0] -1;
|
|
|
|
fprintf(stderr, "Scramble_len [%i]\n", scramble_len);
|
|
fflush(stderr);
|
|
|
|
payload += 11;
|
|
|
|
memcpy(scramble_data_2, payload, scramble_len - 8);
|
|
|
|
fprintf(stderr, "Scramble_buff1[");
|
|
fwrite(scramble_data_1, 8, 1, stderr);
|
|
fprintf(stderr, "]\nScramble_buff2 [");
|
|
fwrite(scramble_data_2, scramble_len - 8, 1, stderr);
|
|
fprintf(stderr, "]\n");
|
|
|
|
fflush(stderr);
|
|
|
|
memcpy(scramble, scramble_data_1, 8);
|
|
memcpy(scramble + 8, scramble_data_2, scramble_len - 8);
|
|
|
|
fprintf(stderr, "Full Scramble 20 bytes is [\n");
|
|
fwrite(scramble, 20, 1, stderr);
|
|
fprintf(stderr, "\n]\n");
|
|
fflush(stderr);
|
|
|
|
memcpy(conn->scramble, scramble, 20);
|
|
fprintf(stderr, "Scramble from MYSQL_Conn is [\n");
|
|
fwrite(scramble, 20, 1, stderr);
|
|
fprintf(stderr, "\n]\n");
|
|
|
|
fflush(stderr);
|
|
|
|
fprintf(stderr, "Now sending user, pass & db\n[");
|
|
|
|
fwrite(&server_capabilities, 4, 1, stderr);
|
|
fprintf(stderr, "]\n");
|
|
|
|
final_capabilities = skysql_get_byte4((uint8_t *)&server_capabilities);
|
|
fprintf(stderr, "CAPABS [%u]\n", final_capabilities);
|
|
fflush(stderr);
|
|
|
|
memset(packet_buffer, '\0', sizeof(packet_buffer));
|
|
//packet_header(byte3 +1 pack#)
|
|
packet_buffer[3] = '\x01';
|
|
|
|
//final_capabilities = 1025669;
|
|
final_capabilities |= SKYSQL_CAPABILITIES_PROTOCOL_41;
|
|
final_capabilities &= SKYSQL_CAPABILITIES_CLIENT;
|
|
|
|
if (passwd != NULL) {
|
|
uint8_t hash1[APR_SHA1_DIGESTSIZE];
|
|
uint8_t hash2[APR_SHA1_DIGESTSIZE];
|
|
uint8_t new_sha[APR_SHA1_DIGESTSIZE];
|
|
|
|
skysql_sha1_str(passwd, strlen(passwd), hash1);
|
|
fprintf(stderr, "Hash1 [%s]\n", hash1);
|
|
skysql_sha1_str(hash1, 20, hash2);
|
|
fprintf(stderr, "Hash2 [%s]\n", hash2);
|
|
|
|
fprintf(stderr, "SHA1(SHA1(password in hex)\n");
|
|
bin2hex(dbpass, hash2, 20);
|
|
fprintf(stderr, "PAss [%s]\n", dbpass);
|
|
fflush(stderr);
|
|
|
|
skysql_sha1_2_str(scramble, 20, hash2, 20, new_sha);
|
|
fprintf(stderr, "newsha [%s]\n", new_sha);
|
|
|
|
skysql_str_xor(client_scramble, new_sha, hash1, 20);
|
|
|
|
fprintf(stderr, "Client send scramble 20 [\n");
|
|
fwrite(client_scramble, 20, 1, stderr);
|
|
fprintf(stderr, "\n]\n");
|
|
fflush(stderr);
|
|
|
|
}
|
|
|
|
if (dbname == NULL) {
|
|
// now without db!!
|
|
final_capabilities &= ~SKYSQL_CAPABILITIES_CONNECT_WITH_DB;
|
|
} else {
|
|
final_capabilities |= SKYSQL_CAPABILITIES_CONNECT_WITH_DB;
|
|
}
|
|
|
|
|
|
skysql_set_byte4(client_capabilities, final_capabilities);
|
|
memcpy(packet_buffer + 4, client_capabilities, 4);
|
|
|
|
packet_buffer[4] = '\x8d';
|
|
packet_buffer[5] = '\xa6';
|
|
packet_buffer[6] = '\x0f';
|
|
packet_buffer[7] = '\x00';
|
|
|
|
skysql_set_byte4(packet_buffer + 4 + 4, 16777216);
|
|
packet_buffer[12] = '\x08';
|
|
|
|
fprintf(stderr, "User is [%s]\n", user);
|
|
fflush(stderr);
|
|
|
|
strcpy(packet_buffer+36, user);
|
|
|
|
fprintf(stderr, "HERE\n");
|
|
fflush(stderr);
|
|
|
|
bytes = 32 + 22 + 1 + 1;
|
|
|
|
bytes += strlen(user);
|
|
|
|
if (dbname == NULL) {
|
|
strcpy(packet_buffer+36 + 5 + 2, "mysql_native_password");
|
|
} else {
|
|
if (passwd != NULL) {
|
|
*(packet_buffer+36 + 5 + 1) = 20;
|
|
memcpy(packet_buffer+36 + 5 + 1 + 1, client_scramble, 20);
|
|
strcpy(packet_buffer+36 + 5 + 1 + 1 + 20, dbname);
|
|
strcpy(packet_buffer+36 + 5 + 1 + 1 + 20 + strlen(dbname) + 1, "mysql_native_password");
|
|
bytes += 20 + strlen(dbname) + 1;
|
|
} else {
|
|
strcpy(packet_buffer+36 + 5 + 1 + 1, dbname);
|
|
strcpy(packet_buffer+36 + 5 + 1 + 1 + strlen(dbname) + 1, "mysql_native_password");
|
|
bytes += strlen(dbname) + 1;
|
|
}
|
|
}
|
|
|
|
skysql_set_byte3(packet_buffer, bytes);
|
|
|
|
bytes += 4;
|
|
|
|
rv = apr_socket_send(socket, packet_buffer, &bytes);
|
|
|
|
if (rv != APR_SUCCESS) {
|
|
fprintf(stderr, "Errore in send\n");
|
|
}
|
|
|
|
bytes = 4096;
|
|
|
|
memset(buffer, '\0', sizeof (buffer));
|
|
|
|
rv = apr_socket_recv(socket, buffer, &bytes);
|
|
|
|
|
|
if (rv != APR_SUCCESS) {
|
|
fprintf(stderr, "Errore in recv\n");
|
|
}
|
|
|
|
fprintf(stderr, "ok packet\[");
|
|
fwrite(buffer, bytes, 1, stderr);
|
|
fprintf(stderr, "]\n");
|
|
fflush(stderr);
|
|
|
|
if (buffer[4] == '\x00') {
|
|
fprintf(stderr, "OK packet received, packet # %i\n", buffer[3]);
|
|
fflush(stderr);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
///////////////////////////////////////
|
|
// interaction with apache scoreboard
|
|
//message 64 bytes max
|
|
///////////////////////////////////////
|
|
|
|
static int update_gateway_child_status(ap_sb_handle_t *sbh, int status, conn_rec *c, apr_bucket_brigade *bb, char *message) {
|
|
worker_score *ws = ap_get_scoreboard_worker(sbh);
|
|
int old_status = ws->status;
|
|
|
|
ws->status = status;
|
|
|
|
if (!ap_extended_status) {
|
|
return old_status;
|
|
}
|
|
|
|
ws->last_used = apr_time_now();
|
|
|
|
/* initial pass only, please - in the name of efficiency */
|
|
if (c) {
|
|
apr_cpystrn(ws->client, ap_get_remote_host(c, c->base_server->lookup_defaults, REMOTE_NOLOOKUP, NULL), sizeof(ws->client));
|
|
apr_cpystrn(ws->vhost, c->base_server->server_hostname, sizeof(ws->vhost));
|
|
/* Deliberate trailing space - filling in string on WRITE passes */
|
|
apr_cpystrn(ws->request, message, sizeof(ws->request));
|
|
}
|
|
|
|
return old_status;
|
|
}
|
|
|
|
///////////////////////////////////////////////////
|
|
// custom mysqlclose for apache styart new child //
|
|
///////////////////////////////////////////////////
|
|
|
|
void child_mysql_close(MYSQL_conn *conn) {
|
|
apr_status_t rv;
|
|
uint8_t packet_buffer[5];
|
|
long bytes = 5;
|
|
|
|
fprintf(stderr, "SkySQL Gateway process ID %lu is exiting\n", getpid());
|
|
fflush(stderr);
|
|
if (conn)
|
|
mysql_close(conn);
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
// custom mysqsl_close in process_connection //
|
|
///////////////////////////////////////////////
|
|
|
|
void my_mysql_close(MYSQL_conn *conn, conn_rec *c) {
|
|
int fd=-1;
|
|
|
|
apr_os_sock_get(&fd,conn->socket);
|
|
|
|
if (fd) {
|
|
if (c !=NULL)
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "Connection TID %lu to backend server closed", conn->tid);
|
|
} else {
|
|
if (c !=NULL)
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "init resources free");
|
|
}
|
|
|
|
mysql_close(conn);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// the mysql protocol implementation at connection level //
|
|
///////////////////////////////////////////////////////////
|
|
|
|
static int skysql_process_connection(conn_rec *c) {
|
|
apr_bucket_brigade *r_bb;
|
|
apr_bucket_brigade *bb;
|
|
apr_bucket *b;
|
|
apr_bucket *auth_bucket;
|
|
apr_bucket *bucket;
|
|
apr_status_t rv;
|
|
int seen_eos = 0;
|
|
int child_stopped_reading = 0;
|
|
|
|
//MYSQL_RES *result;
|
|
//MYSQL_ROW row;
|
|
//MYSQL_STMT *statement = NULL;
|
|
int num_fields;
|
|
int i;
|
|
uint8_t header_result_packet[4];
|
|
uint8_t result_column_count;
|
|
int result_version_len = 0;
|
|
char *query_from_client = NULL;
|
|
int query_from_client_len = 0;
|
|
char *client_auth_packet = NULL;
|
|
unsigned int query_ret = 0;
|
|
int return_data = 0;
|
|
int input_read = 0;
|
|
unsigned int skysql_errno = 0;
|
|
const char *skysql_error_msg = NULL;
|
|
const char *skysql_state = NULL;
|
|
uint8_t *outbuf = NULL;
|
|
uint8_t client_flags[4];
|
|
|
|
int load_balancing_servers = 0;
|
|
int current_slave = -1;
|
|
|
|
skysql_server_conf *conf;
|
|
char *current_slave_server_host = NULL;
|
|
int current_slave_server_port = 3306;
|
|
skysql_client_auth *mysql_client_data = NULL;
|
|
mysql_driver_details *mysql_driver = NULL;
|
|
MYSQL_conn *conn = NULL;
|
|
apr_pool_t *pool = NULL;
|
|
int max_queries_per_connection = 0;
|
|
uint8_t mysql_command = 0;
|
|
char tmp_buffer[10001]="";
|
|
unsigned long tmp_buffer_len = 0L;
|
|
|
|
uint8_t scramble[20]="";
|
|
int scramble_len = 0;
|
|
|
|
uint8_t stage1_hash[20 +1] ="";
|
|
|
|
conn_details *find_server = NULL;
|
|
char *selected_host = NULL;
|
|
char *selected_dbname = NULL;
|
|
int selected_shard = 0;
|
|
int selected_port = 0;
|
|
|
|
apr_interval_time_t timeout = 300000000;
|
|
|
|
/////////////////////////////////////////
|
|
// basic infos from configuration file
|
|
/////////////////////////////////////////
|
|
conf = (skysql_server_conf *)ap_get_module_config(c->base_server->module_config, &skysql_module);
|
|
|
|
///////////////////////////////////////////
|
|
// MYSQL Protocol switch in configuration
|
|
///////////////////////////////////////////
|
|
if (!conf->protocol_enabled) {
|
|
return DECLINED;
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
// now setting the timeout form configuration
|
|
///////////////////////////////////////////////
|
|
|
|
if (conf->loop_timeout > 0) {
|
|
timeout = conf->loop_timeout * 1000000;
|
|
}
|
|
|
|
////////////////////////////////////
|
|
// apache scoreboard update
|
|
// aka, customizing server-status!!
|
|
/////////////////////////////////////
|
|
|
|
ap_time_process_request(c->sbh, START_PREQUEST);
|
|
update_gateway_child_status(c->sbh, SERVER_READY, c, NULL, "GATEWAY: MYSQL ready ");
|
|
|
|
//////////////////////////
|
|
// now the c->pool is ok
|
|
//////////////////////////
|
|
pool = c->pool;
|
|
|
|
///////////////////////////////
|
|
// mysql server/client detail
|
|
///////////////////////////////
|
|
mysql_client_data = apr_pcalloc(pool, sizeof(skysql_client_auth));
|
|
mysql_driver = apr_pcalloc(pool, sizeof(mysql_driver_details));
|
|
mysql_client_data->driver_details = (mysql_driver_details *) mysql_driver;
|
|
|
|
// yeah, one connection
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "I got a connection!, id [%i]", c->id);
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// default scenario is to perform here protocol handshake and the autentication
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////
|
|
// MYSQL 5.1/5.5 Compatible handshake
|
|
// todo: a return structure with connection data: capabilities, scramble_buff
|
|
|
|
update_gateway_child_status(c->sbh, SERVER_BUSY_WRITE, c, NULL, "GATEWAY: MYSQL handshake sent ");
|
|
|
|
rv = skysql_send_handshake(c, scramble, &scramble_len);
|
|
|
|
update_gateway_child_status(c->sbh, SERVER_BUSY_READ, c, NULL, "GATEWAY: MYSQL Auth read ");
|
|
|
|
///////////////////////////////////////
|
|
// now read the client authentication
|
|
// and return data structure with client details, dbname, username, and the stage1_hash
|
|
// the latest is for further backend authentication with same user/pass
|
|
|
|
rv = skysql_read_client_autentication(c, pool, scramble, scramble_len, mysql_client_data, stage1_hash);
|
|
|
|
// client authentication data stored
|
|
|
|
if (!rv) {
|
|
// todo implement custom error packet
|
|
// message and return status
|
|
skysql_send_ok(c, pool, 2, 0, NULL);
|
|
ap_log_error(APLOG_MARK, APLOG_ERR, 0, c->base_server, "*** MySQL Authentication FALSE, thread ID is %i", getpid());
|
|
|
|
return HTTP_INTERNAL_SERVER_ERROR;
|
|
}
|
|
|
|
update_gateway_child_status(c->sbh, SERVER_BUSY_WRITE, c, NULL, "GATEWAY: MYSQL Auth Done ");
|
|
|
|
///////////////////////////////
|
|
// ok, client is autenticated
|
|
// akwnoledge it!
|
|
///////////////////////////////
|
|
skysql_send_ok(c, pool, 2, 0, NULL);
|
|
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "MySQL Authentication OK, thread ID is %i", getpid());
|
|
|
|
//////////////////////////////
|
|
// check if db is in connect
|
|
//////////////////////////////
|
|
if (mysql_driver->connect_with_db) {
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "DB is in connect packet");
|
|
}
|
|
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "current username is [%s]", mysql_client_data->username);
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "current DB is [%s]", mysql_client_data->database != NULL ? mysql_client_data->database : "");
|
|
|
|
// now the pool pointer is set to NULL
|
|
pool = NULL;
|
|
|
|
//////////////////////////
|
|
// check pooling config
|
|
/////////////////////////
|
|
|
|
if (!conf->pool_enabled) {
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "MySQL backend open/close");
|
|
conn = mysql_init(NULL);
|
|
//memset(&conn2, '\0', sizeof(MYSQL_conn));
|
|
//conn = &conn2;
|
|
if (conn == NULL) {
|
|
ap_log_error(APLOG_MARK, APLOG_ERR, 0, c->base_server, "MYSQL init Error %u: %s", 1, "No memory");
|
|
return 500;
|
|
}
|
|
|
|
// do the connect
|
|
// find config data
|
|
find_server = apr_hash_get(conf->resources, "loadbal", APR_HASH_KEY_STRING);
|
|
if (find_server != NULL) {
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "SKYSQL config find was DONE");
|
|
|
|
//switch(find_server->type)
|
|
if (find_server->nshards == 1) {
|
|
selected_port = atoi(strchr(find_server->server_list, ':') + 1);
|
|
selected_host = apr_pstrndup(c->pool, find_server->server_list, strchr(find_server->server_list, ':') - find_server->server_list);
|
|
selected_shard = 1;
|
|
} else {
|
|
selected_shard = select_random_slave_server(find_server->nshards);
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "SKYSQL config find [%i] servers", find_server->nshards);
|
|
get_server_from_list(&selected_host, &selected_port, find_server->server_list, selected_shard, c->pool);
|
|
}
|
|
} else {
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "SKYSQL config find KO: using default!");
|
|
selected_port = 3306;
|
|
selected_host = apr_pstrdup(c->pool, "127.0.0.1");
|
|
}
|
|
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "SKYSQL backend selection [%i], [%s]:[%i]", selected_shard, selected_host, selected_port);
|
|
|
|
if (mysql_client_data->database != NULL) {
|
|
selected_dbname = mysql_client_data->database;
|
|
} else {
|
|
selected_dbname = "test";
|
|
}
|
|
|
|
if (mysql_connect(selected_host, selected_port, selected_dbname, mysql_client_data->username, "pippo", conn) != 0) {
|
|
//if (mysql_real_connect(conn, "192.168.1.40", "root", "pippo", "test", 3306, NULL, 0) == NULL) {
|
|
ap_log_error(APLOG_MARK, APLOG_ERR, 0, c->base_server, "MYSQL Connect [%s:%i] Error %u: %s", selected_host, selected_port, mysql_errno(conn), mysql_error(conn));
|
|
return 500;
|
|
} else {
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "SkySQL RunTime Opened connection to backend");
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "Backend Server TID %i, scamble_buf [%5s]", conn->tid, conn->scramble);
|
|
}
|
|
|
|
} else {
|
|
// use the pool
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "MySQL backend pool");
|
|
conn = conf->conn;
|
|
}
|
|
|
|
update_gateway_child_status(c->sbh, SERVER_BUSY_READ, c, NULL, apr_psprintf(c->pool, "GATEWAY: MYSQL backend selected, DB [%s] ", selected_dbname));
|
|
|
|
//////////////////////////////////////////////////////
|
|
// main loop
|
|
// speaking MySQL protocol 5.1/5.5
|
|
//////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////
|
|
// here applying the timeout to the current socket
|
|
// this protects/saves the main loop
|
|
// so ... choose the right value
|
|
|
|
apr_socket_timeout_set(ap_get_conn_socket(c), timeout);
|
|
|
|
while(1) {
|
|
//////////////////////////////////////////////////////////////
|
|
// the new pool is allocated on c->pool
|
|
// this new pool is the right one for the while(1) main loop
|
|
// it MUST BE destroyed just before exiting the loop, or on
|
|
// a break statement
|
|
// take care of it
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
apr_pool_create(&pool, c->pool);
|
|
|
|
r_bb = apr_brigade_create(pool, c->bucket_alloc);
|
|
|
|
/////////////////////////
|
|
// reading client input
|
|
/////////////////////////
|
|
|
|
child_stopped_reading = 0;
|
|
input_read = 0;
|
|
|
|
update_gateway_child_status(c->sbh, SERVER_BUSY_KEEPALIVE, c, NULL, apr_psprintf(pool, "GATEWAY: MYSQL loop, DB [%s]", selected_dbname));
|
|
|
|
///////////////////////////////////////////////
|
|
// Get input bytes from the client, blocking
|
|
// TODO: handle multi packet input
|
|
// or reading larger data input
|
|
// yes, this is only one brigade!
|
|
///////////////////////////////////////////////
|
|
|
|
if (((rv = ap_get_brigade(c->input_filters, r_bb, AP_MODE_READBYTES, APR_BLOCK_READ, 8192)) != APR_SUCCESS) || APR_BRIGADE_EMPTY(r_bb)) {
|
|
char errmsg[256]="";
|
|
// is this an error?
|
|
//apr_brigade_cleanup(r_bb);
|
|
//apr_brigade_destroy(r_bb);
|
|
apr_strerror(rv, errmsg, sizeof(errmsg));
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, ">>> No more data from client, in ap_get_brigade [%s]", errmsg);
|
|
|
|
//apr_pool_destroy(pool);
|
|
// this breaks the main loop
|
|
//break;
|
|
}
|
|
|
|
/////////////////////////////////////////////
|
|
// now extract data bucket from the brigade
|
|
/////////////////////////////////////////////
|
|
|
|
for (bucket = APR_BRIGADE_FIRST(r_bb); bucket != APR_BRIGADE_SENTINEL(r_bb); bucket = APR_BUCKET_NEXT(bucket)) {
|
|
apr_size_t len = 0;
|
|
const char *data = NULL;
|
|
if (APR_BUCKET_IS_EOS(bucket)) {
|
|
seen_eos = 1;
|
|
break;
|
|
}
|
|
|
|
if (APR_BUCKET_IS_FLUSH(bucket)) {
|
|
continue;
|
|
}
|
|
|
|
if (child_stopped_reading) {
|
|
// the statement breaks this 'for' loop NOT the main loop with 'while'!
|
|
break;
|
|
}
|
|
|
|
///////////////////////////
|
|
// reading a bucket
|
|
// what to do with large input data, as 'mysql load data'????
|
|
///////////////////////////
|
|
rv = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
|
|
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "Input data with len [%i]", len);
|
|
|
|
if (rv != APR_SUCCESS) {
|
|
char errmsg[256]="";
|
|
apr_strerror(rv, errmsg, sizeof(errmsg));
|
|
child_stopped_reading = 1;
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "Child stopped reading [%s]", errmsg);
|
|
}
|
|
////////////////////////////////////////////////////////
|
|
// current data is copied into a pool allocated buffer
|
|
////////////////////////////////////////////////////////
|
|
query_from_client = (char *)apr_pstrmemdup(pool, data, len);
|
|
query_from_client_len = len;
|
|
|
|
input_read = 1;
|
|
}
|
|
|
|
// let's destroy the brigate, it's useless now
|
|
apr_brigade_destroy(r_bb);
|
|
|
|
// now handle client input
|
|
if (input_read == 1 && query_from_client != NULL) {
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "Client Input [%s], command [%x]", query_from_client+5, query_from_client[4]);
|
|
} else {
|
|
// no data read
|
|
// input buffer NULL or empty
|
|
// what to do?
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "SkySQL Gateway main loop: input is empty, exiting");
|
|
apr_pool_destroy(pool);
|
|
break;
|
|
}
|
|
|
|
//prepare custom error response if max is raised
|
|
max_queries_per_connection++;
|
|
if (max_queries_per_connection > 1000002) {
|
|
ap_log_error(APLOG_MARK, APLOG_ERR, 0, c->base_server, "max_queries_per_connection reached = %li", max_queries_per_connection);
|
|
gateway_send_error(c, pool, 1);
|
|
apr_pool_destroy(pool);
|
|
|
|
// if (die_on__max_queries_per_connection)
|
|
//break;
|
|
continue;
|
|
}
|
|
|
|
// check the mysql thread id, for pre-openend the ti is in conf->tid
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "Serving Client with MySQL Thread ID [%lu]", conn->tid);
|
|
|
|
mysql_command = query_from_client[4];
|
|
|
|
/////////////////////////////////////
|
|
// now processing the mysql_command
|
|
/////////////////////////////////////
|
|
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "Client Input command [%x]", mysql_command);
|
|
|
|
update_gateway_child_status(c->sbh, SERVER_BUSY_KEEPALIVE, c, NULL, apr_psprintf(pool, "GATEWAY: MYSQL loop Command [%x], DB [%s]", mysql_command, selected_dbname));
|
|
|
|
switch (mysql_command) {
|
|
case 0x0e :
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "COM_PING");
|
|
// reponse sent directly to the client
|
|
// no ping to backend, for now
|
|
skysql_send_ok(c, pool, 1, 0, NULL);
|
|
break;
|
|
case 0x03 :
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "COM_QUERY");
|
|
skygateway_query_result(c, pool, conn, query_from_client+5);
|
|
//skysql_send_ok(c, pool, 1, 0, NULL);
|
|
|
|
break;
|
|
case 0x02 :
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "COM_INIT_DB");
|
|
//mysql_select_db(conn, query_from_client+5);
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "COM_INIT_DB", query_from_client+5);
|
|
// reponse sent to the client
|
|
skysql_send_ok(c, pool, 1, 0, NULL);
|
|
break;
|
|
case 0x01 :
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "COM_QUIT");
|
|
// QUIT received
|
|
// close backend connection if not pooled
|
|
// and exit the switch
|
|
|
|
if (!conf->pool_enabled) {
|
|
mysql_close(conn);
|
|
}
|
|
break;
|
|
dafault :
|
|
ap_log_error(APLOG_MARK, APLOG_ERR, 0, c->base_server, "UNKNOW MYSQL PROTOCOL COMMAND [%x]", mysql_command);
|
|
// reponse sent to the client, with custom error: TODO
|
|
skysql_send_ok(c, pool, 1, 0, "unknow command");
|
|
break;
|
|
}
|
|
|
|
/////////////////////////
|
|
// now all is done: destroy immediately all resources in the new poll
|
|
// the loop continues with no resources allocated
|
|
apr_pool_destroy(pool);
|
|
|
|
// if COM_QUIT terminate the main loop!
|
|
if (mysql_command == 0x01) {
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "COM_QUIT has been received, the main loop now ends");
|
|
break;
|
|
} else {
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "the main loop continues");
|
|
continue;
|
|
}
|
|
|
|
////////////////////////////
|
|
// main loop now ends
|
|
////////////////////////////
|
|
|
|
}
|
|
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "Main loop ended!");
|
|
|
|
// hey, it was okay to handle the protocol connectioni, is thereanything else to do?
|
|
|
|
update_gateway_child_status(c->sbh, SERVER_CLOSING, c, NULL, "GATEWAY: MYSQL quit ");
|
|
|
|
ap_time_process_request(c->sbh, STOP_PREQUEST);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// The sample content handler
|
|
// Only with HTTP protocol
|
|
// so it's useless now
|
|
// will be useful with JSON
|
|
////////////////////////////////
|
|
static int skysql_handler(request_rec *r)
|
|
{
|
|
if (strcmp(r->handler, "skysql")) {
|
|
return DECLINED;
|
|
}
|
|
r->content_type = "text/html";
|
|
|
|
if (!r->header_only)
|
|
ap_rputs("The sample page from mod_skysql.c\n", r);
|
|
return OK;
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// Module Initialization
|
|
// Persistent structures & data
|
|
/////////////////////////////////
|
|
|
|
static int skysql_init_module(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *base_server) {
|
|
server_rec *s;
|
|
|
|
s = base_server;
|
|
/*
|
|
do initialization here
|
|
*/
|
|
ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, "SKYSQL Init: Internal structure done");
|
|
ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, "SKYSQL Init: ext file ver is [%i]", skysql_ext_file_ver());
|
|
|
|
|
|
return OK;
|
|
}
|
|
|
|
////////////////////////////////////////
|
|
// Child Initialization
|
|
// If enabled, per child connection(s)
|
|
////////////////////////////////////////
|
|
|
|
static void skysql_child_init(apr_pool_t *p, server_rec *s) {
|
|
// take care of virtualhosts ...
|
|
while(s) {
|
|
skysql_server_conf *conf;
|
|
conf = (skysql_server_conf *)ap_get_module_config(s->module_config, &skysql_module);
|
|
|
|
if (conf->protocol_enabled && conf->pool_enabled) {
|
|
|
|
// MySQL Init
|
|
conf->conn = mysql_init(NULL);
|
|
conf->conn->pool = p;
|
|
|
|
// store child process id
|
|
conf->gateway_id = getpid();
|
|
|
|
if (conf->conn == NULL) {
|
|
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "MYSQL init Error %u: %s\n", mysql_errno(conf->conn), mysql_error(conf->conn));
|
|
return;
|
|
}
|
|
if (mysql_connect("127.0.0.1", 3306, "test", "root", "pippo", conf->conn) != 0) {
|
|
//if (mysql_real_connect(conf->conn, "192.168.1.40", "root", "pippo", "test", 3306, NULL, 0) == NULL) {
|
|
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "MYSQL Connect Error %u: %s\n", mysql_errno(conf->conn), mysql_error(conf->conn));
|
|
return ;
|
|
} else {
|
|
conf->mysql_tid = conf->conn->tid;
|
|
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "PID %li SkySQL Child Init & Open connection TID %lu to backend", getpid(), conf->mysql_tid);
|
|
}
|
|
|
|
} else {
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "Generic init flags %i, %i, Skip Protocol Setup & Skip database connection", conf->protocol_enabled, conf->pool_enabled);
|
|
}
|
|
|
|
// next virtual host ..
|
|
s = s->next;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////
|
|
// Creating defaulf configuration data
|
|
////////////////////////////////////////
|
|
static void * create_skysql_config(apr_pool_t *p, server_rec *s) {
|
|
skysql_server_conf *ps = apr_pcalloc(p, sizeof(skysql_server_conf));
|
|
ps->conn = NULL;
|
|
ps->protocol_enabled = 0;
|
|
ps->pool_enabled = 0;
|
|
ps->resources = apr_hash_make(p);
|
|
ps->loop_timeout = 300;
|
|
|
|
return ps;
|
|
}
|
|
|
|
/////////////////////////////
|
|
// Enabling MySQL Protocol //
|
|
/////////////////////////////
|
|
static const char *skysql_protocol_enable(cmd_parms *cmd, void *dummy, int arg)
|
|
{
|
|
skysql_server_conf *sconf = ap_get_module_config(cmd->server->module_config, &skysql_module);
|
|
sconf->protocol_enabled = arg;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// Enabling MySQL loop timeout //
|
|
/////////////////////////////////
|
|
static const char *skysql_loop_timeout(cmd_parms *cmd, void *dummy, const char *arg)
|
|
{
|
|
skysql_server_conf *sconf = ap_get_module_config(cmd->server->module_config, &skysql_module);
|
|
|
|
sconf->loop_timeout = atoi(arg);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/////////////////////////////////////////////
|
|
// Enabling per child persistent connection
|
|
/////////////////////////////////////////////
|
|
static const char *skysql_pool_enable(cmd_parms *cmd, void *dummy, int arg)
|
|
{
|
|
skysql_server_conf *sconf = ap_get_module_config(cmd->server->module_config, &skysql_module);
|
|
sconf->pool_enabled = arg;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static const char *skysql_single_db_resource(cmd_parms *cmd, void *dconf, const char *a1, const char *a2) {
|
|
char *ptr_port = NULL;
|
|
char *ptr_db = NULL;
|
|
char *ptr_host = NULL;
|
|
char *ptr_list = NULL;
|
|
|
|
skysql_server_conf *conf = ap_get_module_config(cmd->server->module_config, &skysql_module);
|
|
|
|
conn_details *newresource = apr_pcalloc(cmd->pool, sizeof(conn_details));
|
|
|
|
newresource->raw_config = apr_pstrdup(cmd->pool, a2);
|
|
newresource->name = apr_pstrdup(cmd->pool, a1);
|
|
|
|
ptr_db = strchr(a2, ';');
|
|
newresource->server_list = apr_pstrndup(cmd->pool, a2, ptr_db-a2);
|
|
newresource->dbname = apr_pstrdup(cmd->pool, ptr_db+1);
|
|
|
|
newresource->nshards = 1;
|
|
|
|
ptr_list = newresource->server_list;
|
|
ptr_host = ptr_list;
|
|
while((ptr_host = strchr(ptr_list, ',')) != NULL) {
|
|
newresource->nshards++;
|
|
ptr_list = ptr_host + 1;
|
|
}
|
|
// now put the struct in the hash table
|
|
apr_hash_set(conf->resources, apr_pstrdup(cmd->pool, a1), APR_HASH_KEY_STRING, newresource);
|
|
|
|
// creare un contenitore, table??? da agganciare con la key a1 e value a2
|
|
fprintf(stderr, "Config Resource %s with %i servers, [%s]\n", a1, newresource->nshards, newresource->server_list);
|
|
fflush(stderr);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//////////////////////////////
|
|
// commands implemeted here //
|
|
//////////////////////////////
|
|
static const command_rec skysql_cmds[] = {
|
|
AP_INIT_FLAG("SkySQLProtocol", skysql_protocol_enable, NULL, RSRC_CONF, "Run an MYSQL protocol on this host"),
|
|
AP_INIT_FLAG("SkySQLPool", skysql_pool_enable, NULL, RSRC_CONF, "SKYSQL backend servers pool"),
|
|
AP_INIT_TAKE2("SkySQLSingleDBbresource", skysql_single_db_resource, NULL, OR_FILEINFO, "a single db resource name"),
|
|
AP_INIT_TAKE1("SkySQLTimeout", skysql_loop_timeout, NULL, OR_FILEINFO, "MYSQL protocol loop timeout"),
|
|
// SkySQLMaxQueryPerConnection
|
|
{NULL}
|
|
};
|
|
|
|
////////////////////////////
|
|
// hooks implemented here //
|
|
////////////////////////////
|
|
static void skysql_register_hooks(apr_pool_t *p)
|
|
{
|
|
ap_hook_post_config(skysql_init_module, NULL,NULL, APR_HOOK_MIDDLE);
|
|
ap_hook_child_init(skysql_child_init, NULL, NULL, APR_HOOK_MIDDLE);
|
|
ap_hook_process_connection(skysql_process_connection, NULL, NULL, APR_HOOK_MIDDLE);
|
|
ap_hook_handler(skysql_handler, NULL, NULL, APR_HOOK_MIDDLE);
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// Dispatch list for API hooks //
|
|
/////////////////////////////////
|
|
|
|
module AP_MODULE_DECLARE_DATA skysql_module = {
|
|
STANDARD20_MODULE_STUFF,
|
|
NULL, /* create per-dir config structures */
|
|
NULL, /* merge per-dir config structures */
|
|
create_skysql_config, /* create per-server config structures */
|
|
NULL, /* merge per-server config structures */
|
|
skysql_cmds, /* table of config file commands */
|
|
skysql_register_hooks /* register hooks */
|
|
};
|
|
|
|
///////////////////////////////////////////////
|