コード例 #1
0
ファイル: tee.c プロジェクト: jhnwkmn/MaxScale
/**
 * The routeQuery entry point. This is passed the query buffer
 * to which the filter should be applied. Once applied the
 * query should normally be passed to the downstream component
 * (filter or router) in the filter chain.
 *
 * If my_session->residual is set then duplicate that many bytes
 * and send them to the branch.
 *
 * If my_session->residual is zero then this must be a new request
 * Extract the SQL text if possible, match against that text and forward
 * the request. If the requets is not contained witin the packet we have
 * then set my_session->residual to the number of outstanding bytes
 *
 * @param instance	The filter instance data
 * @param session	The filter session
 * @param queue		The query data
 */
static	int	
routeQuery(FILTER *instance, void *session, GWBUF *queue)
{
    TEE_INSTANCE	*my_instance = (TEE_INSTANCE *)instance;
    TEE_SESSION		*my_session = (TEE_SESSION *)session;
    char		*ptr;
    int			rval;
    GWBUF		*buffer = NULL, *clone = NULL;
    unsigned char	command = gwbuf_length(queue) >= 5 ?
	*((unsigned char*)queue->start + 4) : 1;

#ifdef SS_DEBUG
    skygw_log_write(LOGFILE_TRACE,"Tee routeQuery: %d : %s",
		    atomic_add(&debug_seq,1),
		    ((char*)queue->start + 5));
#endif


    spinlock_acquire(&my_session->tee_lock);

    if(!my_session->active)
    {
	skygw_log_write(LOGFILE_TRACE, "Tee: Received a reply when the session was closed.");
	gwbuf_free(queue);
	spinlock_release(&my_session->tee_lock);
	return 0;
    }

    if(my_session->queue)
    {
	my_session->queue = gwbuf_append(my_session->queue,queue);
	buffer = modutil_get_next_MySQL_packet(&my_session->queue);
    }
    else
    {
	buffer = modutil_get_next_MySQL_packet(&queue);
	my_session->queue = queue;
    }

    if(buffer == NULL)
    {
	spinlock_release(&my_session->tee_lock);
	return 1;
    }
    
    clone = clone_query(my_instance, my_session,buffer);
    spinlock_release(&my_session->tee_lock);

    /* Reset session state */
    if(!reset_session_state(my_session,buffer))
	return 0;

    /** Route query downstream */
    spinlock_acquire(&my_session->tee_lock);
    rval = route_single_query(my_instance,my_session,buffer,clone);
    spinlock_release(&my_session->tee_lock);

    return rval;
}
コード例 #2
0
ファイル: maxinfo.c プロジェクト: bigshuai/MaxScale
/**
 * We have data from the client, this is a SQL command, or other MySQL
 * packet type.
 *
 * @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 *rinstance, void *router_session, GWBUF *queue)
{
INFO_INSTANCE	*instance = (INFO_INSTANCE *)rinstance;
INFO_SESSION	*session = (INFO_SESSION *)router_session;
uint8_t		*data;
int		length, len, residual;
char		*sql;

	if (GWBUF_TYPE(queue) == GWBUF_TYPE_HTTP)
	{
		return handle_url(instance, session, queue);
	}
	if (session->queue)
	{
		queue = gwbuf_append(session->queue, queue);
		session->queue = NULL;
		queue = gwbuf_make_contiguous(queue);
	}
	data = (uint8_t *)GWBUF_DATA(queue);
	length = data[0] + (data[1] << 8) + (data[2] << 16);
	if (length + 4 > GWBUF_LENGTH(queue))
	{
		// Incomplete packet, must be buffered
		session->queue = queue;
		return 1;
	}

	// We have a complete request in a signle buffer
	if (modutil_MySQL_Query(queue, &sql, &len, &residual))
	{
		sql = strndup(sql, len);
		int rc = maxinfo_execute_query(instance, session, sql);
		free(sql);
		return rc;
	}
	else
	{
		switch (MYSQL_COMMAND(queue))
		{
		case COM_PING:
			return maxinfo_ping(instance, session, queue);
		case COM_STATISTICS:
			return maxinfo_statistics(instance, session, queue);
		default:
                    MXS_ERROR("maxinfo: Unexpected MySQL command 0x%x",
                              MYSQL_COMMAND(queue));
		}
	}

	return 1;
}
コード例 #3
0
ファイル: blr_slave.c プロジェクト: balsdorf/MaxScale
/**
 * 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);
}
コード例 #4
0
ファイル: tee.c プロジェクト: DBMSRmutl/MaxScale
/**
 * The clientReply entry point. This is passed the response buffer
 * to which the filter should be applied. Once processed the
 * query is passed to the upstream component
 * (filter or router) in the filter chain.
 *
 * @param instance	The filter instance data
 * @param session	The filter session
 * @param reply		The response data
 */
static int
clientReply(FILTER* instance, void *session, GWBUF *reply)
{
    int rc, branch, eof;
    TEE_SESSION *my_session = (TEE_SESSION *) session;
    bool route = false, mpkt;
    GWBUF *complete = NULL;
    unsigned char *ptr;
    uint16_t flags = 0;
    int min_eof = my_session->command != 0x04 ? 2 : 1;
    int more_results = 0;
#ifdef SS_DEBUG
    int prev_debug_seq = atomic_add(&debug_seq, 1);
    ptr = (unsigned char*) reply->start;
    MXS_INFO("Tee clientReply [%s] [%s] [%s]: %d",
             instance ? "parent" : "child",
             my_session->active ? "open" : "closed",
             PTR_IS_ERR(ptr) ? "ERR" : PTR_IS_OK(ptr) ? "OK" : "RSET",
             prev_debug_seq);
#endif

    spinlock_acquire(&my_session->tee_lock);

    if (!my_session->active)
    {
        MXS_INFO("Tee: Failed to return reply, session is closed");
        gwbuf_free(reply);
        rc = 0;
        if (my_session->waiting[PARENT])
        {
            GWBUF* errbuf = modutil_create_mysql_err_msg(1, 0, 1, "0000", "Session closed.");
            my_session->waiting[PARENT] = false;
            my_session->up.clientReply(my_session->up.instance,
                                       my_session->up.session,
                                       errbuf);
        }
        goto retblock;
    }

    branch = instance == NULL ? CHILD : PARENT;

    my_session->tee_partials[branch] = gwbuf_append(my_session->tee_partials[branch], reply);
    my_session->tee_partials[branch] = gwbuf_make_contiguous(my_session->tee_partials[branch]);
    complete = modutil_get_complete_packets(&my_session->tee_partials[branch]);

    if (complete == NULL)
    {
        /** Incomplete packet */
        MXS_DEBUG("tee.c: Incomplete packet, "
                  "waiting for a complete packet before forwarding.");
        rc = 1;
        goto retblock;
    }

    complete = gwbuf_make_contiguous(complete);

    if (my_session->tee_partials[branch] &&
        GWBUF_EMPTY(my_session->tee_partials[branch]))
    {
        gwbuf_free(my_session->tee_partials[branch]);
        my_session->tee_partials[branch] = NULL;
    }

    ptr = (unsigned char*) complete->start;

    if (my_session->replies[branch] == 0)
    {
        MXS_INFO("Tee: First reply to a query for [%s].", branch == PARENT ? "PARENT" : "CHILD");
        /* Reply is in a single packet if it is an OK, ERR or LOCAL_INFILE packet.
         * Otherwise the reply is a result set and the amount of packets is unknown.
         */
        if (PTR_IS_ERR(ptr) || PTR_IS_LOCAL_INFILE(ptr) ||
            PTR_IS_OK(ptr) || !my_session->multipacket[branch])
        {
            my_session->waiting[branch] = false;
            my_session->multipacket[branch] = false;
            if (PTR_IS_OK(ptr))
            {
                flags = get_response_flags(ptr, true);
                more_results = (flags & 0x08) && my_session->client_multistatement;
                if (more_results)
                {
                    MXS_INFO("Tee: [%s] waiting for more results.", branch == PARENT ? "PARENT" : "CHILD");
                }
            }
        }
#ifdef SS_DEBUG
        else
        {
            MXS_DEBUG("tee.c: [%ld] Waiting for a result set from %s session.",
                      my_session->d_id,
                      branch == PARENT ? "parent" : "child");
        }
#endif
    }

    if (my_session->waiting[branch])
    {
        eof = modutil_count_signal_packets(complete, my_session->use_ok, my_session->eof[branch] > 0, &more_results);
        more_results &= my_session->client_multistatement;
        my_session->eof[branch] += eof;

        if (my_session->eof[branch] >= min_eof)
        {
#ifdef SS_DEBUG
            MXS_DEBUG("tee.c [%ld] %s received last EOF packet",
                      my_session->d_id,
                      branch == PARENT ? "parent" : "child");
#endif
            my_session->waiting[branch] = more_results;
            if (more_results)
            {
                my_session->eof[branch] = 0;
            }
        }
    }

    if (branch == PARENT)
    {
        my_session->tee_replybuf = gwbuf_append(my_session->tee_replybuf, complete);
    }
    else
    {
        gwbuf_free(complete);
    }

    my_session->replies[branch]++;
    rc = 1;
    mpkt = my_session->multipacket[PARENT] || my_session->multipacket[CHILD];

    if (my_session->tee_replybuf != NULL)
    {

        if (my_session->branch_session == NULL)
        {
            rc = 0;
            gwbuf_free(my_session->tee_replybuf);
            my_session->tee_replybuf = NULL;
            MXS_ERROR("Tee child session was closed.");
        }

        if (mpkt)
        {

            if (my_session->waiting[PARENT])
            {
                route = true;

            }
            else if (my_session->eof[PARENT] >= min_eof &&
                     my_session->eof[CHILD] >= min_eof)
            {
                route = true;
#ifdef SS_DEBUG
                MXS_DEBUG("tee.c:[%ld] Routing final packet of response set.", my_session->d_id);
#endif
            }
        }
        else if (!my_session->waiting[PARENT] &&
                 !my_session->waiting[CHILD])
        {
#ifdef SS_DEBUG
            MXS_DEBUG("tee.c:[%ld] Routing single packet response.", my_session->d_id);
#endif
            route = true;
        }
    }

    if (route)
    {
#ifdef SS_DEBUG
        MXS_DEBUG("tee.c:[%ld] Routing buffer '%p' parent(waiting [%s] replies [%d] eof[%d])"
                  " child(waiting [%s] replies[%d] eof [%d])",
                  my_session->d_id,
                  my_session->tee_replybuf,
                  my_session->waiting[PARENT] ? "true" : "false",
                  my_session->replies[PARENT],
                  my_session->eof[PARENT],
                  my_session->waiting[CHILD] ? "true" : "false",
                  my_session->replies[CHILD],
                  my_session->eof[CHILD]);
#endif

        rc = my_session->up.clientReply(my_session->up.instance,
                                        my_session->up.session,
                                        my_session->tee_replybuf);
        my_session->tee_replybuf = NULL;
    }

    if (my_session->queue &&
        !my_session->waiting[PARENT] &&
        !my_session->waiting[CHILD])
    {
        GWBUF* buffer = modutil_get_next_MySQL_packet(&my_session->queue);
        GWBUF* clone = clone_query(my_session->instance, my_session, buffer);
        reset_session_state(my_session, buffer);
        route_single_query(my_session->instance, my_session, buffer, clone);
        MXS_INFO("tee: routing queued query");

    }

retblock:

    spinlock_release(&my_session->tee_lock);

    return rc;
}
コード例 #5
0
ファイル: blr_slave.c プロジェクト: balsdorf/MaxScale
/**
 * 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;
}
コード例 #6
0
ファイル: httpd.c プロジェクト: markus456/MaxScale
/**
 * 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;
GWBUF		*buf = NULL;
char		*ptr, *sol;
HTTPD_session	*client_data = NULL;
int 		n;

	// Read all the available data
	if ((n = dcb_read(dcb, &buf)) != -1)
	{
		client_data = dcb->data;

		if (client_data->saved)
		{
			buf = gwbuf_append(client_data->saved, buf);
			client_data->saved = NULL;
		}
		buf = gwbuf_make_contiguous(buf);

		ptr = GWBUF_DATA(buf);

		if (strncasecmp(ptr, "POST", 4))
		{
			client_data->method = METHOD_POST;
			gwbuf_add_property(buf, "Method", "POST");
			ptr = ptr + 4;
		}
		else if (strncasecmp(ptr, "PUT", 3))
		{
			client_data->method = METHOD_PUT;
			gwbuf_add_property(buf, "Method", "PUT");
			ptr = ptr + 3;
		}
		else if (strncasecmp(ptr, "GET", 3))
		{
			client_data->method = METHOD_GET;
			gwbuf_add_property(buf, "Method", "GET");
			ptr = ptr + 3;
		}
		else if (strncasecmp(ptr, "HEAD", 4))
		{
			client_data->method = METHOD_HEAD;
			gwbuf_add_property(buf, "Method", "HEAD");
			ptr = ptr + 4;
		}
		while (ptr < (char *)(buf->end) && isspace(*ptr))
			ptr++;
		sol = ptr;
		while (ptr < (char *)(buf->end) && isspace(*ptr) == 0)
			ptr++;
		client_data->url = strndup(sol, ptr - sol);
		gwbuf_add_property(buf, "URL", client_data->url);
		while ((sol = httpd_nextline(buf, ptr)) != NULL && 
				*sol != '\n' && *sol != '\r')
		{
			httpd_process_header(buf, sol, client_data);
			ptr = sol;
		}

		/*
		 * We have read all the headers, or run out of data to 
		 * examine.
		 */
		if (sol == NULL)
		{
			client_data->saved = buf;
			return 0;
		}
		else
		{
			if (((char *)(buf->end) - sol)
					< client_data->request_len)
			{
				client_data->saved = buf;
			}
			else
			{
				LOGIF(LT, (skygw_log_write(
		                           LOGFILE_TRACE,
               		            "HTTPD: request %s.\n", client_data->url)));
				SESSION_ROUTE_QUERY(session, buf);
				if (client_data->url)
				{
					free(client_data->url);
					client_data->url = NULL;
				}
			}
		}



	}
	return 0;
}