Exemplo n.º 1
0
/**
 * Convert a chain of GWBUF structures into a single GWBUF structure
 *
 * @param orig		The chain to convert
 * @return		The contiguous buffer
 */
GWBUF *
gwbuf_make_contiguous(GWBUF *orig)
{
    GWBUF	*newbuf;
    char	*ptr;
    int	len;

    if (orig->next == NULL)
        return orig;

    if ((newbuf = gwbuf_alloc(gwbuf_length(orig))) != NULL)
    {
        newbuf->gwbuf_type = orig->gwbuf_type;
        newbuf->hint = hint_dup(orig->hint);
        ptr = GWBUF_DATA(newbuf);

        while (orig)
        {
            len = GWBUF_LENGTH(orig);
            memcpy(ptr, GWBUF_DATA(orig), len);
            ptr += len;
            orig = gwbuf_consume(orig, len);
        }
    }
    return newbuf;
}
Exemplo n.º 2
0
void manual_query()
{
  char query[1024];
  unsigned int qlen;
  GWBUF** tmpbuf;
 
  free_buffers();

  printf("Enter query: ");
  fgets(query,1024,stdin);

  qlen = strnlen(query, 1024);
  if((tmpbuf = malloc(sizeof(GWBUF*)))== NULL){
    printf("Error: cannot allocate enough memory.\n");
    skygw_log_write(LOGFILE_ERROR,"Error: cannot allocate enough memory.\n");
    return;
  }
  instance.buffer = tmpbuf;
  instance.buffer_count = 1;

  instance.buffer[0] = gwbuf_alloc(qlen + 5);
  gwbuf_set_type(instance.buffer[0],GWBUF_TYPE_MYSQL);
  memcpy(instance.buffer[0]->sbuf->data + 5,query,qlen);

  instance.buffer[0]->sbuf->data[0] = (qlen);
  instance.buffer[0]->sbuf->data[1] = (qlen << 8);
  instance.buffer[0]->sbuf->data[2] = (qlen << 16);
  instance.buffer[0]->sbuf->data[3] = 0x00;
  instance.buffer[0]->sbuf->data[4] = 0x03;

}
Exemplo n.º 3
0
int
test2()
{
GWBUF   *buffer;
char len = 128;
char query[129];

        buffer = gwbuf_alloc(132);
	ss_info_dassert((buffer != NULL),"Buffer should not be null");

	memset(query,';',128);
    memset(query+128,'\0',1);
	*((unsigned char*)buffer->start) = len;
	*((unsigned char*)buffer->start+1) = 0;
	*((unsigned char*)buffer->start+2) = 0;
	*((unsigned char*)buffer->start+3) = 1;
	*((unsigned char*)buffer->start+4) = 0x03;
	memcpy(buffer->start + 5,query,strlen(query));
	char* result = modutil_get_SQL(buffer);
	ss_dassert(strcmp(result,query) == 0);
	gwbuf_free(buffer);
	free(result);
        ss_dfprintf(stderr, "\t..done\n");
	return 0;

}
Exemplo n.º 4
0
static int
test1()
{
GWBUF   *buffer;
char    *(sql[100]);
int     result, length, residual;

        /* Poll tests */  
        ss_dfprintf(stderr,
                    "testmodutil : Rudimentary tests."); 
        buffer = gwbuf_alloc(100);
        ss_info_dassert(0 == modutil_is_SQL(buffer), "Default buffer should be diagnosed as not SQL");
        /* There would ideally be some straightforward way to create a SQL buffer? */
        ss_dfprintf(stderr, "\t..done\nExtract SQL from buffer");
        ss_info_dassert(0 == modutil_extract_SQL(buffer, sql, &length), "Default buffer should fail");
        ss_dfprintf(stderr, "\t..done\nExtract SQL from buffer different way?");
        ss_info_dassert(0 == modutil_MySQL_Query(buffer, sql, &length, &residual), "Default buffer should fail");
        ss_dfprintf(stderr, "\t..done\nReplace SQL in buffer");
        ss_info_dassert(0 == modutil_replace_SQL(buffer, "select * from some_table;"), "Default buffer should fail");
        ss_dfprintf(stderr, "\t..done\nTidy up.");
        gwbuf_free(buffer);
        ss_dfprintf(stderr, "\t..done\n");
		
	return 0;
        
}
Exemplo n.º 5
0
/**
 * Send a response to a "SELECT UNIX_TIMESTAMP()" request. This differs from the other
 * requests since we do not save a copy of the original interaction with the master
 * and simply replay it. We want to always send the current time. We have stored a typcial
 * response, which gives us the schema information normally returned. This is sent to the
 * client and then we add a dynamic part that will insert the current timestamp data.
 * Finally we send a preprepaed EOF packet to end the response stream.
 *
 * @param	router		The binlog router instance
 * @param	slave		The slave server to which we are sending the response
 * @return	Non-zero if data was sent
 */
static int
blr_slave_send_timestamp(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave)
{
GWBUF	*pkt;
char	timestamp[20];
uint8_t *ptr;
int	len, ts_len;

	sprintf(timestamp, "%ld", time(0));
	ts_len = strlen(timestamp);
	len = sizeof(timestamp_def) + sizeof(timestamp_eof) + 5 + ts_len;
	if ((pkt = gwbuf_alloc(len)) == NULL)
		return 0;
	ptr = GWBUF_DATA(pkt);
	memcpy(ptr, timestamp_def, sizeof(timestamp_def));	// Fixed preamble
	ptr += sizeof(timestamp_def);
	encode_value(ptr, ts_len + 1, 24);			// Add length of data packet
	ptr += 3;
	*ptr++ = 0x04;						// Sequence number in response
	*ptr++ = ts_len;					// Length of result string
	strncpy((char *)ptr, timestamp, ts_len);		// Result string
	ptr += ts_len;
	memcpy(ptr, timestamp_eof, sizeof(timestamp_eof));	// EOF packet to terminate result
	return slave->dcb->func.write(slave->dcb, pkt);
}
Exemplo n.º 6
0
/**
 * Process a slave replication registration message.
 *
 * We store the various bits of information the slave gives us and generate
 * a reply message.
 *
 * @param	router		The router instance
 * @param	slave		The slave server
 * @param	queue		The BINLOG_DUMP packet
 * @return			Non-zero if data was sent
 */
static int
blr_slave_register(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue)
{
GWBUF	*resp;
uint8_t	*ptr;
int	len, slen;

	ptr = GWBUF_DATA(queue);
	len = extract_field(ptr, 24);
	ptr += 4;		// Skip length and sequence number
	if (*ptr++ != COM_REGISTER_SLAVE)
		return 0;
	slave->serverid = extract_field(ptr, 32);
	ptr += 4;
	slen = *ptr++;
	if (slen != 0)
	{
		slave->hostname = strndup((char *)ptr, slen);
		ptr += slen;
	}
	else
		slave->hostname = NULL;
	slen = *ptr++;
	if (slen != 0)
	{
		ptr += slen;
		slave->user = strndup((char *)ptr, slen);
	}
	else
		slave->user = NULL;
	slen = *ptr++;
	if (slen != 0)
	{
		slave->passwd = strndup((char *)ptr, slen);
		ptr += slen;
	}
	else
		slave->passwd = NULL;
	slave->port = extract_field(ptr, 16);
	ptr += 2;
	slave->rank = extract_field(ptr, 32);

	/*
	 * Now construct a response
	 */
	if ((resp = gwbuf_alloc(11)) == NULL)
		return 0;
	ptr = GWBUF_DATA(resp);
	encode_value(ptr, 7, 24);	// Payload length
	ptr += 3;
	*ptr++ = 1;			// Sequence number
	encode_value(ptr, 0, 24);
	ptr += 3;
	encode_value(ptr, slave->serverid, 32);
	slave->state = BLRS_REGISTERED;
	return slave->dcb->func.write(slave->dcb, resp);
}
Exemplo n.º 7
0
/**
 * 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);
}
Exemplo n.º 8
0
/**
 * Send the field count packet in a response packet sequence.
 *
 * @param router	The router
 * @param slave		The slave connection
 * @param count		Number of columns in the result set
 * @return		Non-zero on success
 */
static int
blr_slave_send_fieldcount(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, int count)
{
GWBUF	*pkt;
uint8_t *ptr;

	if ((pkt = gwbuf_alloc(5)) == NULL)
		return 0;
	ptr = GWBUF_DATA(pkt);
	encode_value(ptr, 1, 24);			// Add length of data packet
	ptr += 3;
	*ptr++ = 0x01;					// Sequence number in response
	*ptr++ = count;					// Length of result string
	return slave->dcb->func.write(slave->dcb, pkt);
}
Exemplo n.º 9
0
GWBUF* gen_packet(PACKET pkt)
{
	unsigned int psize = 0;
	GWBUF* buff = NULL;
	unsigned char* ptr;
	switch(pkt){
	case PACKET_OK:
		psize = 11;
		break;

	default:
		break;

	}
	if(psize > 0){
		buff = gwbuf_alloc(psize);
		ptr = (unsigned char*)buff->start;
  
		switch(pkt){
		case PACKET_OK:

			ptr[0] = 7; /**Packet size*/
			ptr[1] = 0;
			ptr[2] = 0;

			ptr[3] = 1; /**sequence_id*/

			ptr[4] = 0; /**OK header*/

			ptr[5] = 0; /**affected_rows*/

			ptr[6] = 0; /**last_insert_id*/

			ptr[7] = 0; /**status_flags*/
			ptr[8] = 0;

			ptr[9] = 0; /**warnings*/
			ptr[10] = 0;
			break;

		default:
			break;

		}
	}
	return buff;
}
Exemplo n.º 10
0
int main(int argc, char** argv)
{

	unsigned int psize;
	GWBUF* qbuff;
	char *tok;
	char readbuff[4092];
	FILE* infile;
	FILE* outfile;
	
	if(argc != 3){
		printf("Usage: canonizer <input file> <output file>\n");
		return 1;
	} 

	if(mysql_library_init(num_elements, server_options, server_groups)){
		printf("Embedded server init failed.\n");
		return 1;
	}
	
	infile = fopen(argv[1],"rb");
	outfile = fopen(argv[2],"wb");
	
	if(infile == NULL || outfile == NULL){
		printf("Opening files failed.\n");
		return 1;
	}

	while(!feof(infile))
		{
			fgets(readbuff,4092,infile);
			psize = strlen(readbuff);
			if(psize < 4092){
				qbuff = gwbuf_alloc(psize + 7);
				*(qbuff->sbuf->data + 0) = (unsigned char)psize;
				*(qbuff->sbuf->data + 1) = (unsigned char)(psize>>8);
				*(qbuff->sbuf->data + 2) = (unsigned char)(psize>>16);
				*(qbuff->sbuf->data + 4) = 0x03;
				memcpy(qbuff->start + 5,readbuff,psize + 1);
				parse_query(qbuff);
				tok = skygw_get_canonical(qbuff);
				fprintf(outfile,"%s\n",tok);
				free(tok);
				gwbuf_free(qbuff);
			}			
		}
Exemplo n.º 11
0
/**
 * Send an OK packet to the client.
 * @param dcb The DCB that connects to the client
 */
void maxinfo_send_ok(DCB *dcb)
{
    static const char ok_packet[] ={
        0x07, 0x00, 0x00, 0x01,
        0x00, 0x00, 0x00,
        0x00, 0x00,
        0x00, 0x00
    };

    GWBUF* buffer = gwbuf_alloc(sizeof(ok_packet));

    if (buffer)
    {
        memcpy(buffer->start, ok_packet, sizeof(ok_packet));
        dcb->func.write(dcb, buffer);
    }
}
Exemplo n.º 12
0
/**
 * Respond to a COM_PING command
 *
 * @param router	The router instance
 * @param session	The connection that requested the ping
 * @param queue		The ping request
 */
static int
maxinfo_ping(INFO_INSTANCE *router, INFO_SESSION *session, GWBUF *queue)
{
char	*ptr;
GWBUF	*ret;
int	len;

	if ((ret = gwbuf_alloc(5)) == NULL)
		return 0;
	ptr = GWBUF_DATA(ret);
	*ptr++ = 0x01;
	*ptr++ = 0;
	*ptr++ = 0;
	*ptr++ = 1;
	*ptr = 0;		// OK

	return session->dcb->func.write(session->dcb, ret);
}
Exemplo n.º 13
0
/**
 * Send a "fake" format description event to the newly connected slave
 *
 * @param router	The router instance
 * @param slave		The slave to send the event to
 */
static void
blr_slave_send_fde(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave)
{
BLFILE		*file;
REP_HEADER	hdr;
GWBUF		*record, *head;
uint8_t		*ptr;
uint32_t	chksum;

	if ((file = blr_open_binlog(router, slave->binlogfile)) == NULL)
		return;
	if ((record = blr_read_binlog(router, file, 4, &hdr)) == NULL)
	{
		blr_close_binlog(router, file);
		return;
	}
	blr_close_binlog(router, file);
	head = gwbuf_alloc(5);
	ptr = GWBUF_DATA(head);
	encode_value(ptr, hdr.event_size + 1, 24); // Payload length
	ptr += 3;
	*ptr++ = slave->seqno++;
	*ptr++ = 0;		// OK
	head = gwbuf_append(head, record);
	ptr = GWBUF_DATA(record);
	encode_value(ptr, time(0), 32);		// Overwrite timestamp
	ptr += 13;
	encode_value(ptr, 0, 32);		// Set next position to 0
	/*
	 * Since we have changed the timestamp we must recalculate the CRC
	 *
	 * Position ptr to the start of the event header,
	 * calculate a new checksum
	 * and write it into the header
	 */
	ptr = GWBUF_DATA(record) + hdr.event_size - 4;
	chksum = crc32(0L, NULL, 0);
	chksum = crc32(chksum, GWBUF_DATA(record), hdr.event_size - 4);
	encode_value(ptr, chksum, 32);
	slave->dcb->func.write(slave->dcb, head);
}
Exemplo n.º 14
0
/**
 * Replace the contents of a GWBUF with the new SQL statement passed as a text string.
 * The routine takes care of the modification needed to the MySQL packet,
 * returning a GWBUF chain that can be used to send the data to a MySQL server
 *
 * @param orig	The original request in a GWBUF
 * @param sql	The SQL text to replace in the packet
 * @return A newly formed GWBUF containing the MySQL packet.
 */
GWBUF *
modutil_replace_SQL(GWBUF *orig, char *sql)
{
unsigned char	*ptr;
int	length, newlength;
GWBUF	*addition;

	if (!modutil_is_SQL(orig))
		return NULL;
	ptr = GWBUF_DATA(orig);
	length = *ptr++;
	length += (*ptr++ << 8);
	length += (*ptr++ << 16);
        ptr += 2;  // Skip sequence id	and COM_QUERY byte

	newlength = strlen(sql);
	if (length - 1 == newlength)
	{
		/* New SQL is the same length as old */
		memcpy(ptr, sql, newlength);
	}
	else if (length - 1 > newlength)
	{
		/* New SQL is shorter */
		memcpy(ptr, sql, newlength);
		GWBUF_RTRIM(orig, (length - 1) - newlength);
	}
	else
	{
		memcpy(ptr, sql, length - 1);
		addition = gwbuf_alloc(newlength - (length - 1));
		memcpy(GWBUF_DATA(addition), &sql[length - 1], newlength - (length - 1));
		ptr = GWBUF_DATA(orig);
		*ptr++ = (newlength + 1) & 0xff;
		*ptr++ = ((newlength + 1) >> 8) & 0xff;
		*ptr++ = ((newlength + 1) >> 16) & 0xff;
		orig->next = addition;
	}

	return orig;
}
Exemplo n.º 15
0
/**
 * Construct an error response
 *
 * @param router	The router instance
 * @param slave		The slave server instance
 * @param msg		The error message to send
 */
static void
blr_slave_send_error(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char  *msg)
{
GWBUF		*pkt;
unsigned char   *data;
int             len;

        if ((pkt = gwbuf_alloc(strlen(msg) + 13)) == NULL)
                return;
        data = GWBUF_DATA(pkt);
        len = strlen(msg) + 1;
        encode_value(&data[0], len, 24);	// Payload length
        data[3] = 0;				// Sequence id
						// Payload
        data[4] = 0xff;				// Error indicator
	data[5] = 0;				// Error Code
	data[6] = 0;				// Error Code
	strncpy((char *)&data[7], "#00000", 6);
        memcpy(&data[13], msg, strlen(msg));	// Error Message
	slave->dcb->func.write(slave->dcb, pkt);
}
Exemplo n.º 16
0
/**
 * Send a MySQL OK packet to the DCB
 *
 * @param dcb	The DCB to send the OK packet to
 * @return result of a write call, non-zero if write was successful
 */
static int
maxinfo_send_ok(DCB *dcb)
{
GWBUF	*buf;
uint8_t *ptr;

	if ((buf = gwbuf_alloc(11)) == NULL)
		return 0;
	ptr = GWBUF_DATA(buf);
	*ptr++ = 7;	// Payload length
	*ptr++ = 0;
	*ptr++ = 0;
	*ptr++ = 1;	// Seqno
	*ptr++ = 0;	// ok
	*ptr++ = 0;
	*ptr++ = 0;
	*ptr++ = 2;
	*ptr++ = 0;
	*ptr++ = 0;
	*ptr++ = 0;
	return dcb->func.write(dcb, buf);
}
Exemplo n.º 17
0
/**
 * Send the column definition packet in a response packet sequence.
 *
 * @param router	The router
 * @param slave		The slave connection
 * @param name		Name of the column
 * @param type		Column type
 * @param len		Column length
 * @param seqno		Packet sequence number
 * @return		Non-zero on success
 */
static int
blr_slave_send_columndef(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *name, int type, int len, uint8_t seqno)
{
GWBUF	*pkt;
uint8_t *ptr;

	if ((pkt = gwbuf_alloc(26 + strlen(name))) == NULL)
		return 0;
	ptr = GWBUF_DATA(pkt);
	encode_value(ptr, 22 + strlen(name), 24);	// Add length of data packet
	ptr += 3;
	*ptr++ = seqno;					// Sequence number in response
	*ptr++ = 3;					// Catalog is always def
	*ptr++ = 'd';
	*ptr++ = 'e';
	*ptr++ = 'f';
	*ptr++ = 0;					// Schema name length
	*ptr++ = 0;					// virtal table name length
	*ptr++ = 0;					// Table name length
	*ptr++ = strlen(name);				// Column name length;
	while (*name)
		*ptr++ = *name++;			// Copy the column name
	*ptr++ = 0;					// Orginal column name
	*ptr++ = 0x0c;					// Length of next fields always 12
	*ptr++ = 0x3f;					// Character set
	*ptr++ = 0;
	encode_value(ptr, len, 32);			// Add length of column
	ptr += 4;
	*ptr++ = type;
	*ptr++ = 0x81;					// Two bytes of flags
	if (type == 0xfd)
		*ptr++ = 0x1f;
	else
		*ptr++ = 0x00;
	*ptr++= 0;
	*ptr++= 0;
	*ptr++= 0;
	return slave->dcb->func.write(slave->dcb, pkt);
}
Exemplo n.º 18
0
/**
 * Return some basic statistics from the router in response to a COM_STATISTICS
 * request.
 *
 * @param router	The router instance
 * @param session	The connection that requested the statistics
 * @param queue		The statistics request
 *
 * @return non-zero on sucessful send
 */
static int
maxinfo_statistics(INFO_INSTANCE *router, INFO_SESSION *session, GWBUF *queue)
{
char	result[1000], *ptr;
GWBUF	*ret;
int	len;

	snprintf(result, 1000,
		"Uptime: %u  Threads: %u  Sessions: %u ",
			maxscale_uptime(),
			config_threadcount(),
			serviceSessionCountAll());
	if ((ret = gwbuf_alloc(4 + strlen(result))) == NULL)
		return 0;
	len = strlen(result);
	ptr = GWBUF_DATA(ret);
	*ptr++ = len & 0xff;
	*ptr++ = (len & 0xff00) >> 8;
	*ptr++ = (len & 0xff0000) >> 16;
	*ptr++ = 1;
	strncpy(ptr, result, len);

	return session->dcb->func.write(session->dcb, ret);
}
Exemplo n.º 19
0
/**
 * Loads a query from a file
 *@return 0 if successful, 1 if an error occurred
 */
int load_query()
{
	char** query_list = NULL;
	char* buffer = NULL;
	int i, qcount = 0, qbuff_sz = 10, rval = 0;
	int offset = 0;
	unsigned int qlen = 0;
	buffer = (char*)calloc(4092,sizeof(char));
	if(buffer == NULL){
		printf("Error: cannot allocate enough memory.\n");
		MXS_ERROR("cannot allocate enough memory.\n");
		return 1;
	}

	query_list = calloc(qbuff_sz,sizeof(char*));
	if(query_list == NULL){
		printf("Error: cannot allocate enough memory.\n");
		MXS_ERROR("cannot allocate enough memory.\n");
		free(buffer);
		return 1;
	}


	while((offset = fdgets(instance.infile,buffer,4092))){

		if(qbuff_sz <= qcount){
			char** tmpbuff = realloc(query_list,sizeof(char*)*qbuff_sz*2);
			if(tmpbuff == NULL){
				printf("Error: cannot allocate enough memory.\n");
				MXS_ERROR("cannot allocate enough memory.\n");
				rval = 1;
				goto retblock;
			}
			
			query_list = tmpbuff;
			qbuff_sz *= 2;
			
		}
		
		query_list[qcount] = calloc((offset + 1),sizeof(char));
		strcpy(query_list[qcount],buffer);
		offset = 0;
		qcount++;
		
	}

	/**TODO check what messes up the first querystring*/
	GWBUF** tmpbff = malloc(sizeof(GWBUF*)*(qcount + 1));
	if(tmpbff){
		for(i = 0;i<qcount;i++){
    
			tmpbff[i] = gwbuf_alloc(strlen(query_list[i]) + 6);

			if(tmpbff[i] == NULL)
				{
					printf("Error: cannot allocate a new buffer.\n");
					MXS_ERROR("cannot allocate a new buffer.\n");
					int x;
					for(x = 0;x<i;x++)
						{
							gwbuf_free(tmpbff[x]);
						}
					free(tmpbff);
					rval = 1;
					goto retblock;
				}

			gwbuf_set_type(tmpbff[i],GWBUF_TYPE_MYSQL);
			strcpy((char*)(tmpbff[i]->start + 5),query_list[i]);
			qlen = strlen(query_list[i]) + 1;
			tmpbff[i]->sbuf->data[0] = qlen;
			tmpbff[i]->sbuf->data[1] = (qlen << 8);
			tmpbff[i]->sbuf->data[2] = (qlen << 16);
			tmpbff[i]->sbuf->data[3] = 0x00;
			tmpbff[i]->sbuf->data[4] = 0x03;

		}
		tmpbff[qcount] = NULL;
		instance.buffer = tmpbff;
	}else{
		printf("Error: cannot allocate enough memory for buffers.\n");
		MXS_ERROR("cannot allocate enough memory for buffers.\n");    
		free_buffers();
	    rval = 1;
		goto retblock;
	}

	if(qcount < 1){
		rval = 1;
		goto retblock;
	}
  
	instance.buffer_count = qcount;

	retblock:

	for(i = 0;i<qcount;i++)
		{
			free(query_list[i]);
		}
	free(query_list);
	free(buffer);
	return rval;
}
Exemplo n.º 20
0
/**
 * mysql_send_auth_error
 *
 * Send a MySQL protocol ERR message, for gateway authentication error to the dcb
 *
 * @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_auth_error (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 mysql_err[2];
        uint8_t mysql_statemsg[6];
        unsigned int mysql_errno = 0;
        const char *mysql_error_msg = NULL;
        const char *mysql_state = NULL;

        GWBUF   *buf;

        if (dcb->state != DCB_STATE_POLLING)
        {
                LOGIF(LD, (skygw_log_write(
                        LOGFILE_DEBUG,
                        "%lu [mysql_send_auth_error] dcb %p is in a state %s, "
                        "and it is not in epoll set anymore. Skip error sending.",
                        pthread_self(),
                        dcb,
                        STRDCBSTATE(dcb->state))));
                return 0;
        }
        mysql_errno = 1045;
        mysql_error_msg = "Access denied!";
        mysql_state = "2800";

        field_count = 0xff;
        gw_mysql_set_byte2(mysql_err, mysql_errno);
        mysql_statemsg[0]='#';
        memcpy(mysql_statemsg+1, mysql_state, 5);

        if (mysql_message != NULL) {
                mysql_error_msg = mysql_message;
        }

        mysql_payload_size = sizeof(field_count) + sizeof(mysql_err) + sizeof(mysql_statemsg) + strlen(mysql_error_msg);

        // 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);

        // write field
        memcpy(mysql_payload, &field_count, sizeof(field_count));
        mysql_payload = mysql_payload + sizeof(field_count);

        // write errno
        memcpy(mysql_payload, mysql_err, sizeof(mysql_err));
        mysql_payload = mysql_payload + sizeof(mysql_err);

        // write sqlstate
        memcpy(mysql_payload, mysql_statemsg, sizeof(mysql_statemsg));
        mysql_payload = mysql_payload + sizeof(mysql_statemsg);

        // write err messg
        memcpy(mysql_payload, mysql_error_msg, strlen(mysql_error_msg));

        // writing data in the Client buffer queue
        dcb->func.write(dcb, buf);

        return sizeof(mysql_packet_header) + mysql_payload_size;
}
Exemplo n.º 21
0
/**
 * mysql_send_custom_error
 *
 * Send a MySQL protocol Generic ERR message, to the dcb
 * Note the errno and state are still fixed now
 *
 * @param dcb Owner_Dcb 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_custom_error (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 mysql_err[2];
        uint8_t mysql_statemsg[6];
        unsigned int mysql_errno = 0;
        const char *mysql_error_msg = NULL;
        const char *mysql_state = NULL;

        GWBUF   *buf = NULL;

        if (dcb == NULL ||
            dcb->state != DCB_STATE_POLLING)
        {
                return 0;
        }
        mysql_errno = 2003;
        mysql_error_msg = "An errorr occurred ...";
        mysql_state = "HY000";

        field_count = 0xff;
        gw_mysql_set_byte2(mysql_err, mysql_errno);
        mysql_statemsg[0]='#';
        memcpy(mysql_statemsg+1, mysql_state, 5);

        if (mysql_message != NULL) {
                mysql_error_msg = mysql_message;
        }

        mysql_payload_size = sizeof(field_count) + sizeof(mysql_err) + sizeof(mysql_statemsg) + strlen(mysql_error_msg);

        // allocate memory for packet header + payload
        buf = gwbuf_alloc(sizeof(mysql_packet_header) + mysql_payload_size);
        ss_dassert(buf != NULL);
        
        if (buf == 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);

        // write field
        memcpy(mysql_payload, &field_count, sizeof(field_count));
        mysql_payload = mysql_payload + sizeof(field_count);

        // write errno
        memcpy(mysql_payload, mysql_err, sizeof(mysql_err));
        mysql_payload = mysql_payload + sizeof(mysql_err);

        // write sqlstate
        memcpy(mysql_payload, mysql_statemsg, sizeof(mysql_statemsg));
        mysql_payload = mysql_payload + sizeof(mysql_statemsg);

        // write err messg
        memcpy(mysql_payload, mysql_error_msg, strlen(mysql_error_msg));

        // writing data in the Client buffer queue
        dcb->func.write(dcb, buf);

        return sizeof(mysql_packet_header) + mysql_payload_size;
}
Exemplo n.º 22
0
/**
 * Write a MySQL CHANGE_USER packet to backend server
 *
 * @param conn  MySQL protocol structure
 * @param dbname The selected database
 * @param user The selected user
 * @param passwd The SHA1(real_password): Note real_password is unknown
 * @return 1 on success, 0 on failure
 */
int gw_send_change_user_to_backend(char *dbname, char *user, uint8_t *passwd, MySQLProtocol *conn) {
        int compress = 0;
        int rv;
        uint8_t *payload = NULL;
        uint8_t *payload_start = NULL;
        long bytes;
        uint8_t client_scramble[GW_MYSQL_SCRAMBLE_SIZE];
        uint8_t client_capabilities[4];
        uint32_t server_capabilities;
        uint32_t final_capabilities;
        char dbpass[MYSQL_USER_MAXLEN + 1]="";
	GWBUF *buffer;
	DCB *dcb;

        char *curr_db = NULL;
        uint8_t *curr_passwd = NULL;

        if (strlen(dbname))
                curr_db = dbname;

        if (strlen((char *)passwd))
                curr_passwd = passwd;

	dcb = conn->owner_dcb;

	// Zero the vars
	memset(&server_capabilities, '\0', sizeof(server_capabilities));
	memset(&final_capabilities, '\0', sizeof(final_capabilities));

        final_capabilities = gw_mysql_get_byte4((uint8_t *)&server_capabilities);

        final_capabilities |= GW_MYSQL_CAPABILITIES_PROTOCOL_41;
        final_capabilities |= GW_MYSQL_CAPABILITIES_CLIENT;

        if (compress) {
                final_capabilities |= GW_MYSQL_CAPABILITIES_COMPRESS;
#ifdef DEBUG_MYSQL_CONN
                fprintf(stderr, ">>>> Backend Connection with compression\n");
#endif
        }

        if (curr_passwd != NULL) {
                uint8_t hash1[GW_MYSQL_SCRAMBLE_SIZE]="";
                uint8_t hash2[GW_MYSQL_SCRAMBLE_SIZE]="";
                uint8_t new_sha[GW_MYSQL_SCRAMBLE_SIZE]="";

		// hash1 is the function input, SHA1(real_password)
                memcpy(hash1, passwd, GW_MYSQL_SCRAMBLE_SIZE);

		// hash2 is the SHA1(input data), where input_data = SHA1(real_password)
                gw_sha1_str(hash1, GW_MYSQL_SCRAMBLE_SIZE, hash2);

		// dbpass is the HEX form of SHA1(SHA1(real_password))
                gw_bin2hex(dbpass, hash2, GW_MYSQL_SCRAMBLE_SIZE);

		// new_sha is the SHA1(CONCAT(scramble, hash2)
                gw_sha1_2_str(conn->scramble, GW_MYSQL_SCRAMBLE_SIZE, hash2, GW_MYSQL_SCRAMBLE_SIZE, new_sha);

		// compute the xor in client_scramble
                gw_str_xor(client_scramble, new_sha, hash1, GW_MYSQL_SCRAMBLE_SIZE);

        }

        if (curr_db == NULL) {
                // without db
                final_capabilities &= ~GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB;
        } else {
                final_capabilities |= GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB;
        }

        final_capabilities |= GW_MYSQL_CAPABILITIES_PLUGIN_AUTH;

        gw_mysql_set_byte4(client_capabilities, final_capabilities);

	// Protocol MySQL COM_CHANGE_USER for CLIENT_PROTOCOL_41
	// 1 byte COMMAND
        bytes = 1;

	// add the user
        bytes += strlen(user);
        // the NULL
        bytes++;

	// next will be + 1 (scramble_len) + 20 (fixed_scramble) + (dbname + NULL term) + 2 bytes charset 

        if (curr_passwd != NULL) {
                bytes += GW_MYSQL_SCRAMBLE_SIZE;
                bytes++;
	} else {
                bytes++;
	}	

        if (curr_db != NULL) {
                bytes += strlen(curr_db);
        	bytes++;
	}

	// the charset
	bytes += 2;
        bytes += strlen("mysql_native_password");
        bytes++;

        // the packet header
        bytes += 4;

	// allocating the GWBUF
	buffer = gwbuf_alloc(bytes);
	payload = GWBUF_DATA(buffer);

	// clearing data
	memset(payload, '\0', bytes);
	
	// save the start pointer
	payload_start = payload;

	// set packet # = 1
        payload[3] = 0x00;
        payload += 4;

	// set the command COM_CHANGE_USER \x11
	payload[0] = 0x11;
        payload++;

	memcpy(payload, user, strlen(user));
        payload += strlen(user);
        payload++;

        if (curr_passwd != NULL) {
                // set the auth-length
                *payload = GW_MYSQL_SCRAMBLE_SIZE;
                payload++;

                //copy the 20 bytes scramble data after packet_buffer+36+user+NULL+1 (byte of auth-length)
                memcpy(payload, client_scramble, GW_MYSQL_SCRAMBLE_SIZE);

                payload += GW_MYSQL_SCRAMBLE_SIZE;

        } else {
                // skip the auth-length and write a NULL
                payload++;
        }

        // if the db is not NULL append it
        if (curr_db != NULL) {
                memcpy(payload, curr_db, strlen(curr_db));
                payload += strlen(curr_db);
                payload++;
        }

        // set the charset, 2 bytes!!!!
        *payload = '\x08';
        payload++;
        *payload = '\x00';
        payload++;

        memcpy(payload, "mysql_native_password", strlen("mysql_native_password"));

        payload += strlen("mysql_native_password");
        payload++;

	// put here the paylod size: bytes to write - 4 bytes packet header
        gw_mysql_set_byte3(payload_start, (bytes-4));

	rv = dcb->func.write(dcb, buffer);

	if (rv == 0)
		return 0;
	else
		return 1;
}
Exemplo n.º 23
0
/**
 * 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] = "";
    size_t i, j;
    int headers_read = 0;
    HTTPD_session *client_data = NULL;
    GWBUF *uri;

    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;
    }

    i = 0;

    while ( (j < sizeof(buf)) && ISspace(buf[j]))
    {
        j++;
    }

    while ((j < sizeof(buf) - 1) && !ISspace(buf[j]) && (i < sizeof(url) - 1))
    {
        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 == '?')
        {
            *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);
            }
        }
    }

    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);

#if 0
    /**
     * ToDO: launch proper content handling based on the requested URI, later REST interface
     *
     */
    if (strcmp(url, "/show") == 0)
    {
        if (query_string && strlen(query_string))
        {
            if (strcmp(query_string, "dcb") == 0)
            {
                dprintAllDCBs(dcb);
            }
            if (strcmp(query_string, "session") == 0)
            {
                dprintAllSessions(dcb);
            }
        }
    }
    if (strcmp(url, "/services") == 0)
    {
        RESULTSET *set, *seviceGetList();
        if ((set = serviceGetList()) != NULL)
        {
            resultset_stream_json(set, dcb);
            resultset_free(set);
        }
    }
#endif
    if ((uri = gwbuf_alloc(strlen(url) + 1)) != NULL)
    {
        strcpy((char *)GWBUF_DATA(uri), url);
        gwbuf_set_type(uri, GWBUF_TYPE_HTTP);
        SESSION_ROUTE_QUERY(session, uri);
    }

    /* force the client connecton close */
    dcb_close(dcb);

    return 0;
}
Exemplo n.º 24
0
int main(int argc, char** argv)
{
	if(argc < 3){
		fprintf(stderr,"Usage: classify <input> <expected output>");
		return 1;
	}
	int rd = 0,buffsz = getpagesize(),strsz = 0,ex_val = 0;
	char buffer[1024], *strbuff = (char*)calloc(buffsz,sizeof(char));
	FILE *input,*expected;

	if(mysql_library_init(num_elements, server_options, server_groups))
		{
			printf("Error: Cannot initialize Embedded Library.");
		    return 1;
		}

	input = fopen(argv[1],"rb");

    if(input == NULL)
    {
        printf("Error: Failed to open input file %s", argv[1]);
        return 1;
    }
    
	expected = fopen(argv[2],"rb");

    if(expected == NULL)
    {
        fclose(input);
        printf("Error: Failed to open expected output file %s", argv[2]);
        return 1;
    }

	while((rd = fread(buffer,sizeof(char),1023,input))){
		
		/**Fill the read buffer*/

		if(strsz + rd >= buffsz){

			char* tmp = realloc(strbuff,(buffsz*2)*sizeof(char));
			
			if(tmp == NULL){
				free(strbuff);
				fclose(input);
				fclose(expected);
				mysql_library_end();
				fprintf(stderr,"Error: Memory allocation failed.");
				return 1;
			}
			strbuff = tmp;
			buffsz *= 2;
		}
		
		memcpy(strbuff+strsz,buffer,rd);
		strsz += rd;
		*(strbuff+strsz) = '\0';

		char *tok,*nlptr;

		/**Remove newlines*/
		while((nlptr = strpbrk(strbuff,"\n")) != NULL && (nlptr - strbuff) < strsz){
			memmove(nlptr,nlptr+1,strsz - (nlptr + 1 - strbuff));
			strsz -= 1;
		}


		/**Parse read buffer for full queries*/

		while(strpbrk(strbuff,";") != NULL){
			tok = strpbrk(strbuff,";");
			unsigned int qlen = tok - strbuff + 1;
			GWBUF* buff = gwbuf_alloc(qlen+6);
			*((unsigned char*)(buff->start)) = qlen;
			*((unsigned char*)(buff->start + 1)) = (qlen >> 8);
			*((unsigned char*)(buff->start + 2)) = (qlen >> 16);
			*((unsigned char*)(buff->start + 3)) = 0x00;
			*((unsigned char*)(buff->start + 4)) = 0x03;
			memcpy(buff->start+5, strbuff, qlen);
			memmove(strbuff,tok + 1, strsz - qlen);
			strsz -= qlen;
			memset(strbuff + strsz,0,buffsz - strsz);
			skygw_query_type_t type = query_classifier_get_type(buff);
			char qtypestr[64];
			char expbuff[256];
			int expos = 0;
			
			while((rd = fgetc(expected)) != '\n' && !feof(expected)){
				expbuff[expos++] = rd;
			}
			expbuff[expos] = '\0';

			if(type == QUERY_TYPE_UNKNOWN){
				sprintf(qtypestr,"QUERY_TYPE_UNKNOWN");
			}
			if(type & QUERY_TYPE_LOCAL_READ){
				sprintf(qtypestr,"QUERY_TYPE_LOCAL_READ");
			}
			if(type & QUERY_TYPE_READ){
				sprintf(qtypestr,"QUERY_TYPE_READ");
			}
			if(type & QUERY_TYPE_WRITE){
				sprintf(qtypestr,"QUERY_TYPE_WRITE");
			}
			if(type & QUERY_TYPE_MASTER_READ){
				sprintf(qtypestr,"QUERY_TYPE_MASTER_READ");
			}
			if(type & QUERY_TYPE_SESSION_WRITE){
				sprintf(qtypestr,"QUERY_TYPE_SESSION_WRITE");
			}
			if(type & QUERY_TYPE_USERVAR_READ){
				sprintf(qtypestr,"QUERY_TYPE_USERVAR_READ");
			}
			if(type & QUERY_TYPE_SYSVAR_READ){
				sprintf(qtypestr,"QUERY_TYPE_SYSVAR_READ");
			}
			if(type & QUERY_TYPE_GSYSVAR_READ){
				sprintf(qtypestr,"QUERY_TYPE_GSYSVAR_READ");
			}
			if(type & QUERY_TYPE_GSYSVAR_WRITE){
				sprintf(qtypestr,"QUERY_TYPE_GSYSVAR_WRITE");
			}
			if(type & QUERY_TYPE_BEGIN_TRX){
				sprintf(qtypestr,"QUERY_TYPE_BEGIN_TRX");
			}
			if(type & QUERY_TYPE_ENABLE_AUTOCOMMIT){
				sprintf(qtypestr,"QUERY_TYPE_ENABLE_AUTOCOMMIT");
			}
			if(type & QUERY_TYPE_DISABLE_AUTOCOMMIT){
				sprintf(qtypestr,"QUERY_TYPE_DISABLE_AUTOCOMMIT");
			}
			if(type & QUERY_TYPE_ROLLBACK){
				sprintf(qtypestr,"QUERY_TYPE_ROLLBACK");
			}
			if(type & QUERY_TYPE_COMMIT){
				sprintf(qtypestr,"QUERY_TYPE_COMMIT");
			}
			if(type & QUERY_TYPE_PREPARE_NAMED_STMT){
				sprintf(qtypestr,"QUERY_TYPE_PREPARE_NAMED_STMT");
			}
			if(type & QUERY_TYPE_PREPARE_STMT){
				sprintf(qtypestr,"QUERY_TYPE_PREPARE_STMT");
			}
			if(type & QUERY_TYPE_EXEC_STMT){
				sprintf(qtypestr,"QUERY_TYPE_EXEC_STMT");
			}
			if(type & QUERY_TYPE_CREATE_TMP_TABLE){
				sprintf(qtypestr,"QUERY_TYPE_CREATE_TMP_TABLE");
			}
			if(type & QUERY_TYPE_READ_TMP_TABLE){
				sprintf(qtypestr,"QUERY_TYPE_READ_TMP_TABLE");
			}
			
			if(strcmp(qtypestr,expbuff) != 0){
				printf("Error in output: '%s' was expected but got '%s'",expbuff,qtypestr);
				ex_val = 1;
			}
			
			gwbuf_free(buff);			
		}
	}
	fclose(input);
	fclose(expected);
	mysql_library_end();
	free(strbuff);
	return ex_val;
}
Exemplo n.º 25
0
/**
 *  Generate an internal rotate event that we can use to cause the slave to move beyond
 * a binlog file that is misisng the rotate eent at the end.
 *
 * @param router	The router instance
 * @param slave		The slave to rotate
 * @return  Non-zero if the rotate took place
 */
static int
blr_slave_fake_rotate(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave)
{
char		*sptr;
int		filenum;
GWBUF		*resp;
uint8_t		*ptr;
int		len, binlognamelen;
REP_HEADER	hdr;
uint32_t	chksum;

	if ((sptr = strrchr(slave->binlogfile, '.')) == NULL)
		return 0;
	blr_close_binlog(router, slave->file);
	filenum = atoi(sptr + 1);
	sprintf(slave->binlogfile, BINLOG_NAMEFMT, router->fileroot, filenum + 1);
	slave->binlog_pos = 4;
	if ((slave->file = blr_open_binlog(router, slave->binlogfile)) == NULL)
		return 0;

	binlognamelen = strlen(slave->binlogfile);

	if (slave->nocrc)
		len = 19 + 8 + binlognamelen;
	else
		len = 19 + 8 + 4 + binlognamelen;

	// Build a fake rotate event
	resp = gwbuf_alloc(len + 5);
	hdr.payload_len = len + 1;
	hdr.seqno = slave->seqno++;
	hdr.ok = 0;
	hdr.timestamp = 0L;
	hdr.event_type = ROTATE_EVENT;
	hdr.serverid = router->masterid;
	hdr.event_size = len;
	hdr.next_pos = 0;
	hdr.flags = 0x20;
	ptr = blr_build_header(resp, &hdr);
	encode_value(ptr, slave->binlog_pos, 64);
	ptr += 8;
	memcpy(ptr, slave->binlogfile, binlognamelen);
	ptr += binlognamelen;

	if (!slave->nocrc)
	{
		/*
		 * Now add the CRC to the fake binlog rotate event.
		 *
		 * The algorithm is first to compute the checksum of an empty buffer
		 * and then the checksum of the event portion of the message, ie we do not
		 * include the length, sequence number and ok byte that makes up the first
		 * 5 bytes of the message. We also do not include the 4 byte checksum itself.
		 */
		chksum = crc32(0L, NULL, 0);
		chksum = crc32(chksum, GWBUF_DATA(resp) + 5, hdr.event_size - 4);
		encode_value(ptr, chksum, 32);
	}

	slave->dcb->func.write(slave->dcb, resp);
	return 1;
}
Exemplo n.º 26
0
/**
 * We have a registered slave that is behind the current leading edge of the 
 * binlog. We must replay the log entries to bring this node up to speed.
 *
 * There may be a large number of records to send to the slave, the process
 * is triggered by the slave COM_BINLOG_DUMP message and all the events must
 * be sent without receiving any new event. This measn there is no trigger into
 * MaxScale other than this initial message. However, if we simply send all the
 * events we end up with an extremely long write queue on the DCB and risk
 * running the server out of resources.
 *
 * The slave catchup routine will send a burst of replication events per single
 * call. The paramter "long" control the number of events in the burst. The
 * short burst is intended to be used when the master receive an event and 
 * needs to put the slave into catchup mode. This prevents the slave taking
 * too much tiem away from the thread that is processing the master events.
 *
 * At the end of the burst a fake EPOLLOUT event is added to the poll event
 * queue. This ensures that the slave callback for processing DCB write drain
 * will be called and future catchup requests will be handled on another thread.
 *
 * @param	router		The binlog router
 * @param	slave		The slave that is behind
 * @param	large		Send a long or short burst of events
 * @return			The number of bytes written
 */
int
blr_slave_catchup(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, bool large)
{
GWBUF		*head, *record;
REP_HEADER	hdr;
int		written, rval = 1, burst;
int		rotating;
unsigned long	burst_size;
uint8_t		*ptr;

	if (large)
		burst = router->long_burst;
	else
		burst = router->short_burst;
	burst_size = router->burst_size;
	spinlock_acquire(&slave->catch_lock);
	if (slave->cstate & CS_BUSY)
	{
		spinlock_release(&slave->catch_lock);
		return 0;
	}
	slave->cstate |= CS_BUSY;
	spinlock_release(&slave->catch_lock);

	if (slave->file == NULL)
	{
		rotating = router->rotating;
		if ((slave->file = blr_open_binlog(router, slave->binlogfile)) == NULL)
		{
			if (rotating)
			{
				spinlock_acquire(&slave->catch_lock);
				slave->cstate |= CS_EXPECTCB;
				slave->cstate &= ~CS_BUSY;
				spinlock_release(&slave->catch_lock);
				poll_fake_write_event(slave->dcb);
				return rval;
			}
			LOGIF(LE, (skygw_log_write(
				LOGFILE_ERROR,
				"blr_slave_catchup failed to open binlog file %s",
					slave->binlogfile)));
			slave->cstate &= ~CS_BUSY;
			slave->state = BLRS_ERRORED;
			dcb_close(slave->dcb);
			return 0;
		}
	}
	slave->stats.n_bursts++;
	while (burst-- && burst_size > 0 &&
		(record = blr_read_binlog(router, slave->file, slave->binlog_pos, &hdr)) != NULL)
	{
		head = gwbuf_alloc(5);
		ptr = GWBUF_DATA(head);
		encode_value(ptr, hdr.event_size + 1, 24);
		ptr += 3;
		*ptr++ = slave->seqno++;
		*ptr++ = 0;		// OK
		head = gwbuf_append(head, record);
		if (hdr.event_type == ROTATE_EVENT)
		{
unsigned long beat1 = hkheartbeat;
			blr_close_binlog(router, slave->file);
if (hkheartbeat - beat1 > 1) LOGIF(LE, (skygw_log_write(
                                        LOGFILE_ERROR, "blr_close_binlog took %d beats",
				hkheartbeat - beat1)));
			blr_slave_rotate(slave, GWBUF_DATA(record));
beat1 = hkheartbeat;
			if ((slave->file = blr_open_binlog(router, slave->binlogfile)) == NULL)
			{
				if (rotating)
				{
					spinlock_acquire(&slave->catch_lock);
					slave->cstate |= CS_EXPECTCB;
					slave->cstate &= ~CS_BUSY;
					spinlock_release(&slave->catch_lock);
					poll_fake_write_event(slave->dcb);
					return rval;
				}
				LOGIF(LE, (skygw_log_write(
					LOGFILE_ERROR,
					"blr_slave_catchup failed to open binlog file %s",
					slave->binlogfile)));
				slave->state = BLRS_ERRORED;
				dcb_close(slave->dcb);
				break;
			}
if (hkheartbeat - beat1 > 1) LOGIF(LE, (skygw_log_write(
                                        LOGFILE_ERROR, "blr_open_binlog took %d beats",
				hkheartbeat - beat1)));
		}
		slave->stats.n_bytes += gwbuf_length(head);
		written = slave->dcb->func.write(slave->dcb, head);
		if (written && hdr.event_type != ROTATE_EVENT)
		{
			slave->binlog_pos = hdr.next_pos;
		}
		rval = written;
		slave->stats.n_events++;
		burst_size -= hdr.event_size;
	}
	if (record == NULL)
		slave->stats.n_failed_read++;
	spinlock_acquire(&slave->catch_lock);
	slave->cstate &= ~CS_BUSY;
	spinlock_release(&slave->catch_lock);

	if (record)
	{
		slave->stats.n_flows++;
		spinlock_acquire(&slave->catch_lock);
		slave->cstate |= CS_EXPECTCB;
		spinlock_release(&slave->catch_lock);
		poll_fake_write_event(slave->dcb);
	}
	else if (slave->binlog_pos == router->binlog_position &&
			strcmp(slave->binlogfile, router->binlog_name) == 0)
	{
		int state_change = 0;
		spinlock_acquire(&router->binlog_lock);
		spinlock_acquire(&slave->catch_lock);

		/*
		 * Now check again since we hold the router->binlog_lock
		 * and slave->catch_lock.
		 */
		if (slave->binlog_pos != router->binlog_position ||
			strcmp(slave->binlogfile, router->binlog_name) != 0)
		{
			slave->cstate &= ~CS_UPTODATE;
			slave->cstate |= CS_EXPECTCB;
			spinlock_release(&slave->catch_lock);
			spinlock_release(&router->binlog_lock);
			poll_fake_write_event(slave->dcb);
		}
		else
		{
			if ((slave->cstate & CS_UPTODATE) == 0)
			{
				slave->stats.n_upd++;
				slave->cstate |= CS_UPTODATE;
				spinlock_release(&slave->catch_lock);
				spinlock_release(&router->binlog_lock);
				state_change = 1;
			}
		}

		if (state_change)
		{
			slave->stats.n_caughtup++;
			if (slave->stats.n_caughtup == 1)
			{
				LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE,
					"%s: Slave %s is up to date %s, %u.",
					router->service->name,
					slave->dcb->remote,
					slave->binlogfile, slave->binlog_pos)));
			}
			else if ((slave->stats.n_caughtup % 50) == 0)
			{
				LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE,
					"%s: Slave %s is up to date %s, %u.",
					router->service->name,
					slave->dcb->remote,
					slave->binlogfile, slave->binlog_pos)));
			}
		}
	}
	else
	{
		if (slave->binlog_pos >= blr_file_size(slave->file)
				&& router->rotating == 0
				&& strcmp(router->binlog_name, slave->binlogfile) != 0
				&& blr_master_connected(router))
		{
			/* We may have reached the end of file of a non-current
			 * binlog file.
			 *
			 * Note if the master is rotating there is a window during
			 * which the rotate event has been written to the old binlog
			 * but the new binlog file has not yet been created. Therefore
			 * we ignore these issues during the rotate processing.
			 */
			LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
				"Slave reached end of file for binlong file %s at %u "
				"which is not the file currently being downloaded. "
				"Master binlog is %s, %lu.",
				slave->binlogfile, slave->binlog_pos,
				router->binlog_name, router->binlog_position)));
			if (blr_slave_fake_rotate(router, slave))
			{
				spinlock_acquire(&slave->catch_lock);
				slave->cstate |= CS_EXPECTCB;
				spinlock_release(&slave->catch_lock);
				poll_fake_write_event(slave->dcb);
			}
			else
			{
				slave->state = BLRS_ERRORED;
				dcb_close(slave->dcb);
			}
		}
		else
		{
			spinlock_acquire(&slave->catch_lock);
			slave->cstate |= CS_EXPECTCB;
			spinlock_release(&slave->catch_lock);
			poll_fake_write_event(slave->dcb);
		}
	}
	return rval;
}
Exemplo n.º 27
0
/**
 * Process a COM_BINLOG_DUMP message from the slave. This is the
 * final step in the process of registration. The new master, MaxScale
 * must send a response packet and generate a fake BINLOG_ROTATE event
 * with the binlog file requested by the slave. And then send a
 * FORMAT_DESCRIPTION_EVENT that has been saved from the real master.
 *
 * Once send MaxScale must continue to send binlog events to the slave.
 *
 * @param	router		The router instance
 * @param	slave		The slave server
 * @param	queue		The BINLOG_DUMP packet
 * @return			The number of bytes written to the slave
 */
static int
blr_slave_binlog_dump(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue)
{
GWBUF		*resp;
uint8_t		*ptr;
int		len, flags, serverid, rval, binlognamelen;
REP_HEADER	hdr;
uint32_t	chksum;

	ptr = GWBUF_DATA(queue);
	len = extract_field(ptr, 24);
	binlognamelen = len - 11;
	ptr += 4;		// Skip length and sequence number
	if (*ptr++ != COM_BINLOG_DUMP)
	{
        	LOGIF(LE, (skygw_log_write(
			LOGFILE_ERROR,
			"blr_slave_binlog_dump expected a COM_BINLOG_DUMP but received %d",
			*(ptr-1))));
		return 0;
	}

	slave->binlog_pos = extract_field(ptr, 32);
	ptr += 4;
	flags = extract_field(ptr, 16);
	ptr += 2;
	serverid = extract_field(ptr, 32);
	ptr += 4;
	strncpy(slave->binlogfile, (char *)ptr, binlognamelen);
	slave->binlogfile[binlognamelen] = 0;

	slave->seqno = 1;


	if (slave->nocrc)
		len = 19 + 8 + binlognamelen;
	else
		len = 19 + 8 + 4 + binlognamelen;

	// Build a fake rotate event
	resp = gwbuf_alloc(len + 5);
	hdr.payload_len = len + 1;
	hdr.seqno = slave->seqno++;
	hdr.ok = 0;
	hdr.timestamp = 0L;
	hdr.event_type = ROTATE_EVENT;
	hdr.serverid = router->masterid;
	hdr.event_size = len;
	hdr.next_pos = 0;
	hdr.flags = 0x20;
	ptr = blr_build_header(resp, &hdr);
	encode_value(ptr, slave->binlog_pos, 64);
	ptr += 8;
	memcpy(ptr, slave->binlogfile, binlognamelen);
	ptr += binlognamelen;

	if (!slave->nocrc)
	{
		/*
		 * Now add the CRC to the fake binlog rotate event.
		 *
		 * The algorithm is first to compute the checksum of an empty buffer
		 * and then the checksum of the event portion of the message, ie we do not
		 * include the length, sequence number and ok byte that makes up the first
		 * 5 bytes of the message. We also do not include the 4 byte checksum itself.
		 */
		chksum = crc32(0L, NULL, 0);
		chksum = crc32(chksum, GWBUF_DATA(resp) + 5, hdr.event_size - 4);
		encode_value(ptr, chksum, 32);
	}

	rval = slave->dcb->func.write(slave->dcb, resp);

	/* Send the FORMAT_DESCRIPTION_EVENT */
	if (slave->binlog_pos != 4)
		blr_slave_send_fde(router, slave);

	slave->dcb->low_water  = router->low_water;
	slave->dcb->high_water = router->high_water;
	dcb_add_callback(slave->dcb, DCB_REASON_DRAINED, blr_slave_callback, slave);
	slave->state = BLRS_DUMPING;

	LOGIF(LM, (skygw_log_write(
		LOGFILE_MESSAGE,
			"%s: New slave %s, server id %d,  requested binlog file %s from position %lu",
				router->service->name, slave->dcb->remote,
					slave->serverid,
					slave->binlogfile, slave->binlog_pos)));

	if (slave->binlog_pos != router->binlog_position ||
			strcmp(slave->binlogfile, router->binlog_name) != 0)
	{
		spinlock_acquire(&slave->catch_lock);
		slave->cstate &= ~CS_UPTODATE;
		slave->cstate |= CS_EXPECTCB;
		spinlock_release(&slave->catch_lock);
		poll_fake_write_event(slave->dcb);
	}
	return rval;
}