Beispiel #1
0
/**
 * Close sessions that have been idle for too long.
 *
 * If the time since a session last sent data is greater than the set value in the
 * service, it is disconnected. The connection timeout is disabled by default.
 */
void process_idle_sessions()
{
    if (spinlock_acquire_nowait(&timeout_lock))
    {
        if (hkheartbeat >= next_timeout_check)
        {
            /** Because the resolution of the timeout is one second, we only need to
             * check for it once per second. One heartbeat is 100 milliseconds. */
            next_timeout_check = hkheartbeat + 10;
            spinlock_acquire(&session_spin);
            SESSION *all_session = get_all_sessions();

            while (all_session)
            {
                if (all_session->service && all_session->client_dcb && all_session->client_dcb->state == DCB_STATE_POLLING &&
                    hkheartbeat - all_session->client_dcb->last_read > all_session->service->conn_idle_timeout * 10)
                {
                    dcb_close(all_session->client_dcb);
                }

                all_session = all_session->next;
            }
            spinlock_release(&session_spin);
        }
        spinlock_release(&timeout_lock);
    }
}
Beispiel #2
0
/**
 * Close a session with the router, this is the mechanism
 * by which a router may cleanup data structure etc.
 *
 * @param instance		The router instance data
 * @param router_session	The session being closed
 */
static	void 	
closeSession(ROUTER *instance, void *router_session)
{
ROUTER_CLIENT_SES *router_cli_ses = (ROUTER_CLIENT_SES *)router_session;
DCB*              backend_dcb;

        CHK_CLIENT_RSES(router_cli_ses);
        /**
         * Lock router client session for secure read and update.
         */
        if (rses_begin_locked_router_action(router_cli_ses))
        {
		/* decrease server current connection counter */
		atomic_add(&router_cli_ses->backend->server->stats.n_current, -1);

                backend_dcb = router_cli_ses->backend_dcb;
                router_cli_ses->backend_dcb = NULL;
                router_cli_ses->rses_closed = true;
                /** Unlock */
                rses_end_locked_router_action(router_cli_ses);
                
                /**
                 * Close the backend server connection
                 */
                if (backend_dcb != NULL) {
                        CHK_DCB(backend_dcb);
                        dcb_close(backend_dcb);
                }
        }
}
Beispiel #3
0
/**
 * Send the menu page
 *
 * @param session	The router session
 */
static void
send_menu(WEB_SESSION	*session)
{
DCB	*dcb = session->session->client_dcb;

	send_html_header(dcb);
	send_static_html(dcb, menu_page);
	dcb_close(dcb);
}
Beispiel #4
0
/**
 * Send a blank page
 *
 * @param session	The router session
 */
static void
send_blank(WEB_SESSION	*session)
{
DCB	*dcb = session->session->client;

	send_html_header(dcb);
	send_static_html(dcb, blank_page);
	dcb_close(dcb);
}
Beispiel #5
0
/**
 * Send the CSS
 *
 * @param session	The router session
 */
static void
send_css(WEB_SESSION	*session)
{
DCB	*dcb = session->session->client;

	send_html_header(dcb);
	send_static_html(dcb, css);
	dcb_close(dcb);
}
Beispiel #6
0
static int
telnetd_close(DCB *dcb)
{
TELNETD *telnetd = dcb->protocol;

	if (telnetd && telnetd->username)
		free(telnetd->username);

	dcb_close(dcb);
	return 0;
}
Beispiel #7
0
static int
test1()
{
DCB     *dcb;
int     result;
	int eno = 0;

        /* Poll tests */  
        ss_dfprintf(stderr,
                    "testpoll : Initialise the polling system."); 
        init_test_env(NULL);
        poll_init();
        ss_dfprintf(stderr, "\t..done\nAdd a DCB");
        dcb = dcb_alloc(DCB_ROLE_REQUEST_HANDLER);

		if(dcb == NULL){
			ss_dfprintf(stderr, "\nError on function call: dcb_alloc() returned NULL.\n");
			return 1;
		}

        dcb->fd = socket(AF_UNIX, SOCK_STREAM, 0);

        if(dcb->fd < 0){
                        char errbuf[STRERROR_BUFLEN];
			ss_dfprintf(stderr, "\nError on function call: socket() returned %d: %s\n",errno,strerror_r(errno,errbuf,sizeof(errbuf)));
				    return 1;
		}


        if((eno = poll_add_dcb(dcb)) != 0){
			ss_dfprintf(stderr, "\nError on function call: poll_add_dcb() returned %d.\n",eno);
				    return 1;
		}

        if((eno = poll_remove_dcb(dcb)) != 0){
			ss_dfprintf(stderr, "\nError on function call: poll_remove_dcb() returned %d.\n",eno);
				    return 1;
		}

        if((eno = poll_add_dcb(dcb)) != 0){
			ss_dfprintf(stderr, "\nError on function call: poll_add_dcb() returned %d.\n",eno);
				    return 1;
		}

        ss_dfprintf(stderr, "\t..done\nStart wait for events.");
        sleep(10);
        poll_shutdown();
        ss_dfprintf(stderr, "\t..done\nTidy up.");
        dcb_close(dcb);
        ss_dfprintf(stderr, "\t..done\n");
		
	return 0;
        
}
Beispiel #8
0
/**
 * Respond with an HTTP error
 *
 * @param session	The router session
 * @param err		The HTTP error code to send
 * @param msg		The message to print
 */
static void
respond_error(WEB_SESSION *session, int err, char *msg)
{
DCB	*dcb = session->session->client;

	dcb_printf(dcb, "HTTP/1.1 %d %s\n", err, msg);
	dcb_printf(dcb, "Content-Type: text/html\n");
	dcb_printf(dcb, "\n");
	dcb_printf(dcb, "<HTML><BODY>\n");
	dcb_printf(dcb, "MaxScale webserver plugin unable to satisfy request.\n");
	dcb_printf(dcb, "<P>Code: %d, %s\n", err, msg);
	dcb_printf(dcb, "</BODY></HTML>");
	dcb_close(dcb);
}
Beispiel #9
0
/**
 * Send the monitors page. This iterates on all the monitors and send
 * the rows via the monitor_monitor.
 *
 * @param session	The router session
 */
static void
send_monitors(WEB_SESSION *session)
{
DCB	*dcb = session->session->client;

	send_html_header(dcb);
	dcb_printf(dcb, "<HTML><HEAD>");
	dcb_printf(dcb, "<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">");
	dcb_printf(dcb, "<BODY><H2>Monitors</H2><P>");
	dcb_printf(dcb, "<TABLE><TR><TH>Monitor</TH><TH>State</TH></TR>\n");
	monitorIterate(monitor_row, dcb);
	dcb_printf(dcb, "</TABLE></BODY></HTML>\n");
	dcb_close(dcb);
}
Beispiel #10
0
/**
 * Handler for the EPOLLIN event when the DCB refers to the listening
 * socket for the protocol.
 *
 * @param dcb   The descriptor control block
 */
static int httpd_accept(DCB *dcb)
{
    int n_connect = 0;

    while (1)
    {
        int so = -1;
        struct sockaddr_in addr;
        socklen_t addrlen;
        DCB *client = NULL;
        HTTPD_session *client_data = NULL;

        if ((so = accept(dcb->fd, (struct sockaddr *)&addr, &addrlen)) == -1)
        {
            return n_connect;
        }
        else
        {
            atomic_add(&dcb->stats.n_accepts, 1);

            if ((client = dcb_alloc(DCB_ROLE_REQUEST_HANDLER)))
            {
                client->listen_ssl = dcb->listen_ssl;
                client->fd = so;
                client->remote = strdup(inet_ntoa(addr.sin_addr));
                memcpy(&client->func, &MyObject, sizeof(GWPROTOCOL));

                /* create the session data for HTTPD */
                client_data = (HTTPD_session *)calloc(1, sizeof(HTTPD_session));
                client->data = client_data;

                client->session = session_alloc(dcb->session->service, client);

                if (NULL == client->session || poll_add_dcb(client) == -1)
                {
                    close(so);
                    dcb_close(client);
                    return n_connect;
                }
                n_connect++;
            }
            else
            {
                close(so);
            }
        }
    }

    return n_connect;
}
Beispiel #11
0
/**
 * test1	Allocate a dcb and do lots of other things
 *
  */
static int
test1()
{
DCB   *dcb, *extra, *clone;
int     size = 100;
int     bite1 = 35;
int     bite2 = 60;
int     bite3 = 10;
int     buflen;

        /* Single buffer tests */
        ss_dfprintf(stderr,
                    "testdcb : creating buffer with type DCB_ROLE_SERVICE_LISTENER"); 
        dcb = dcb_alloc(DCB_ROLE_SERVICE_LISTENER);
        printDCB(dcb);
        ss_info_dassert(dcb_isvalid(dcb), "New DCB must be valid");
        ss_dfprintf(stderr, "\t..done\nAllocated dcb.");
        clone = dcb_clone(dcb);
        ss_dfprintf(stderr, "\t..done\nCloned dcb");
        printAllDCBs();
        ss_info_dassert(true, "Something is true");
        ss_dfprintf(stderr, "\t..done\n");
        dcb_close(dcb);
        ss_dfprintf(stderr, "Freed original dcb");
        ss_info_dassert(!dcb_isvalid(dcb), "Freed DCB must not be valid");
        ss_dfprintf(stderr, "\t..done\nMake clone DCB a zombie");
        clone->state = DCB_STATE_NOPOLLING;
        dcb_close(clone);
        ss_info_dassert(dcb_get_zombies() == clone, "Clone DCB must be start of zombie list now");
        ss_dfprintf(stderr, "\t..done\nProcess the zombies list");
        dcb_process_zombies(0);
        ss_dfprintf(stderr, "\t..done\nCheck clone no longer valid");
        ss_info_dassert(!dcb_isvalid(clone), "After zombie processing, clone DCB must not be valid");
        ss_dfprintf(stderr, "\t..done\n");
		
	return 0;
}
Beispiel #12
0
/**
 * Send the servers page
 *
 * @param session	The router session
 */
static void
send_servers(WEB_SESSION *session)
{
DCB	*dcb = session->session->client;

	send_html_header(dcb);
	dcb_printf(dcb, "<HTML><HEAD>");
	dcb_printf(dcb, "<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">");
	dcb_printf(dcb, "<BODY><H2>Servers</H2><P>");
	dcb_printf(dcb, "<TABLE><TR><TH>Server</TH><TH>Address</TH><TH>");
	dcb_printf(dcb, "Port</TH><TH>State</TH><TH>Connections</TH></TR>\n");
	serverIterate(server_row, dcb);
	dcb_printf(dcb, "</TABLE></BODY></HTML>\n");
	dcb_close(dcb);
}
Beispiel #13
0
/**
 * Send the services page. This produces a table by means of the 
 * serviceIterate call.
 *
 * @param session	The router session
 */
static void
send_services(WEB_SESSION *session)
{
DCB	*dcb = session->session->client;

	send_html_header(dcb);
	dcb_printf(dcb, "<HTML><HEAD>");
	dcb_printf(dcb, "<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">");
	dcb_printf(dcb, "<BODY><H2>Services</H2><P>");
	dcb_printf(dcb, "<TABLE><TR><TH>Name</TH><TH>Router</TH><TH>");
	dcb_printf(dcb, "Current Sessions</TH><TH>Total Sessions</TH></TR>\n");
	serviceIterate(service_row, dcb);
	dcb_printf(dcb, "</TABLE></BODY></HTML>\n");
	dcb_close(dcb);
}
Beispiel #14
0
/**
 * Handler for the EPOLLERR event.
 *
 * @param dcb	The descriptor control block
 */
static int
httpd_error(DCB *dcb)
{
HTTPD_session	*client_data = NULL;
	if (dcb->data)
	{
		client_data = dcb->data;
		if (client_data->url)
		{
			free(client_data->url);
			client_data->url = NULL;
		}
		free(dcb->data);
		dcb->data = NULL;
	}
	dcb_close(dcb);
	return 0;
}
Beispiel #15
0
/**
 * Error Handler routine
 *
 * The routine will handle errors that occurred in backend writes.
 *
 * @param       instance        The router instance
 * @param       router_session  The router session
 * @param       message         The error message to reply
 * @param       backend_dcb     The backend DCB
 * @param       action     	The action: ERRACT_NEW_CONNECTION or ERRACT_REPLY_CLIENT
 * @param	succp		Result of action: true iff router can continue
 *
 */
static void handleError(
	ROUTER           *instance,
	void             *router_session,
	GWBUF            *errbuf,
	DCB              *backend_dcb,
	error_action_t   action,
	bool             *succp)

{
	DCB             *client_dcb;
	SESSION         *session = backend_dcb->session;
	session_state_t sesstate;

	/** Don't handle same error twice on same DCB */
	if (backend_dcb->dcb_errhandle_called)
	{
		/** we optimistically assume that previous call succeed */
		*succp = true;
		return;
	}
	else
	{
		backend_dcb->dcb_errhandle_called = true;
	}
	spinlock_acquire(&session->ses_lock);
	sesstate = session->state;
	client_dcb = session->client_dcb;

	if (sesstate == SESSION_STATE_ROUTER_READY)
	{
		CHK_DCB(client_dcb);
		spinlock_release(&session->ses_lock);
		client_dcb->func.write(client_dcb, gwbuf_clone(errbuf));
	}
	else
	{
		spinlock_release(&session->ses_lock);
	}

	/** false because connection is not available anymore */
        dcb_close(backend_dcb);
	*succp = false;
}
Beispiel #16
0
/**
 * We have data from the client, we must route it to the backend.
 * This is simply a case of sending it to the connection that was
 * chosen when we started the client session.
 *
 * @param instance		The router instance
 * @param router_session	The router session returned from the newSession call
 * @param queue			The queue of data buffers to route
 * @return The number of bytes sent
 */
static	int	
execute(ROUTER *instance, void *router_session, GWBUF *queue)
{
CLI_SESSION	*session = (CLI_SESSION *)router_session;

	/* Extract the characters */
	while (queue)
	{
		strncat(session->cmdbuf, GWBUF_DATA(queue), GWBUF_LENGTH(queue));
		queue = gwbuf_consume(queue, GWBUF_LENGTH(queue));
	}

	if (strrchr(session->cmdbuf, '\n'))
	{
		if (execute_cmd(session))
			dcb_printf(session->session->client, "MaxScale> ");
		else
                        dcb_close(session->session->client);
	}
	return 1;
}
Beispiel #17
0
/**
 * Start an individual port/protocol pair
 *
 * @param service	The service
 * @param port		The port to start
 * @return		The number of listeners started
 */
static int
serviceStartPort(SERVICE *service, SERV_PROTOCOL *port)
{
int		listeners = 0;
char		config_bind[40];
GWPROTOCOL	*funcs;

        port->listener = dcb_alloc(DCB_ROLE_SERVICE_LISTENER);

        if (port->listener == NULL)
	{
		LOGIF(LE, (skygw_log_write_flush(
			LOGFILE_ERROR,
			"Error : Failed to create listener for service %s.",
			service->name)));
		goto retblock;
	}
	
	if (strcmp(port->protocol, "MySQLClient") == 0) {
		int loaded;

		if (service->users == NULL) {
			/*
			 * Allocate specific data for MySQL users
			 * including hosts and db names
			 */
			service->users = mysql_users_alloc();
	
			if ((loaded = load_mysql_users(service)) < 0)
			{
				LOGIF(LE, (skygw_log_write_flush(
					LOGFILE_ERROR,
					"Error : Unable to load users from %s:%d for "
					"service %s.",
					(port->address == NULL ? "0.0.0.0" : port->address),
					port->port,
					service->name)));
				
				{
					/* Try loading authentication data from file cache */
					char	*ptr, path[4097];
					strcpy(path, "/usr/local/mariadb-maxscale");
					if ((ptr = getenv("MAXSCALE_HOME")) != NULL)
					{
						strncpy(path, ptr, 4096);
					}
					strncat(path, "/", 4096);
					strncat(path, service->name, 4096);
					strncat(path, "/.cache/dbusers", 4096);
					loaded = dbusers_load(service->users, path);
					if (loaded != -1)
					{
						LOGIF(LE, (skygw_log_write_flush(
							LOGFILE_ERROR,
							"Using cached credential information.")));
					}
				}
				if (loaded == -1)
				{
					hashtable_free(service->users->data);
					free(service->users);
					dcb_free(port->listener);
					port->listener = NULL;
					goto retblock;
				}
			}
			else
			{
				/* Save authentication data to file cache */
				char	*ptr, path[4097];
                                int mkdir_rval = 0;
				strcpy(path, "/usr/local/mariadb-maxscale");
				if ((ptr = getenv("MAXSCALE_HOME")) != NULL)
				{
					strncpy(path, ptr, 4096);
				}
				strncat(path, "/", 4096);
				strncat(path, service->name, 4096);
				if (access(path, R_OK) == -1)
                                {
					mkdir_rval = mkdir(path, 0777);
                                }

                                if(mkdir_rval)
                                {
                                    skygw_log_write(LOGFILE_ERROR,"Error : Failed to create directory '%s': [%d] %s",
                                                    path,
                                                    errno,
                                                    strerror(errno));
                                    mkdir_rval = 0;
                                }

				strncat(path, "/.cache", 4096);
				if (access(path, R_OK) == -1)
                                {
					mkdir_rval = mkdir(path, 0777);
                                }

                                if(mkdir_rval)
                                {
                                    skygw_log_write(LOGFILE_ERROR,"Error : Failed to create directory '%s': [%d] %s",
                                                    path,
                                                    errno,
                                                    strerror(errno));
                                    mkdir_rval = 0;
                                }
				strncat(path, "/dbusers", 4096);
				dbusers_save(service->users, path);
			}
			if (loaded == 0)
			{
				LOGIF(LE, (skygw_log_write_flush(
					LOGFILE_ERROR,
					"Service %s: failed to load any user "
					"information. Authentication will "
					"probably fail as a result.",
					service->name)));
			}

			/* At service start last update is set to USERS_REFRESH_TIME seconds earlier.
 			 * This way MaxScale could try reloading users' just after startup
 			 */
			service->rate_limit.last=time(NULL) - USERS_REFRESH_TIME;
			service->rate_limit.nloads=1;

			LOGIF(LM, (skygw_log_write(
				LOGFILE_MESSAGE,
				"Loaded %d MySQL Users for service [%s].",
				loaded, service->name)));
		}
	} 
	else 
	{
		if (service->users == NULL) {
			/* Generic users table */
			service->users = users_alloc();
		}
	}

	if ((funcs=(GWPROTOCOL *)load_module(port->protocol, MODULE_PROTOCOL)) 
		== NULL)
	{
		if (service->users->data)
		{
			hashtable_free(service->users->data);
		}
		free(service->users);
		dcb_free(port->listener);
		port->listener = NULL;
		LOGIF(LE, (skygw_log_write_flush(
                        LOGFILE_ERROR,
			"Error : Unable to load protocol module %s. Listener "
                        "for service %s not started.",
			port->protocol,
                        service->name)));
		goto retblock;
	}
	memcpy(&(port->listener->func), funcs, sizeof(GWPROTOCOL));
	port->listener->session = NULL;
	
	if (port->address)
		sprintf(config_bind, "%s:%d", port->address, port->port);
	else
		sprintf(config_bind, "0.0.0.0:%d", port->port);

	if (port->listener->func.listen(port->listener, config_bind)) 
	{
                port->listener->session = session_alloc(service, port->listener);

                if (port->listener->session != NULL) 
		{
                        port->listener->session->state = SESSION_STATE_LISTENER;
                        listeners += 1;
                } 
                else 
		{
			LOGIF(LE, (skygw_log_write_flush(
				LOGFILE_ERROR,
				"Error : Failed to create session to service %s.",
				service->name)));
			
			if (service->users->data)
			{
				hashtable_free(service->users->data);
			}
			free(service->users);
                        dcb_close(port->listener);
			port->listener = NULL;
			goto retblock;
                }
        } 
        else 
	{       
                LOGIF(LE, (skygw_log_write_flush(
                        LOGFILE_ERROR,
			"Error : Unable to start to listen port %d for %s %s.",
			port->port,
                        port->protocol,
                        service->name)));
		if (service->users->data)
		{
			hashtable_free(service->users->data);
		}
		free(service->users);
		dcb_close(port->listener);
		port->listener = NULL;
        }
        
retblock:
	return listeners;
}
Beispiel #18
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] = "";
int cgi = 0;
size_t i, j;
int headers_read = 0;
HTTPD_session *client_data = NULL;

	client_data = dcb->data;

	/**
	 * get the request line
	 * METHOD URL HTTP_VER\r\n
	 */

	numchars = httpd_get_line(dcb->fd, buf, sizeof(buf));

	i = 0; j = 0;
	while (!ISspace(buf[j]) && (i < sizeof(method) - 1)) {
		method[i] = buf[j];
		i++; j++;
	}
	method[i] = '\0';

	strcpy(client_data->method, method);

	/* check allowed http methods */
	if (strcasecmp(method, "GET") && strcasecmp(method, "POST")) {
		//httpd_unimplemented(dcb->fd);
		return 0;
	}

	if (strcasecmp(method, "POST") == 0)
		cgi = 1;

	i = 0;

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

	while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf))) {
		url[i] = buf[j];
		i++; j++;
	}

	url[i] = '\0';

	/**
	 * Get the query string if availble
	 */

	if (strcasecmp(method, "GET") == 0) {
		query_string = url;
		while ((*query_string != '?') && (*query_string != '\0'))
			query_string++;
		if (*query_string == '?') {
			cgi = 1;
			*query_string = '\0';
			query_string++;
		}
	}

	/**
	 * Get the request headers
	 */

	while ((numchars > 0) && strcmp("\n", buf)) {
		char *value = NULL;
		char *end = NULL;
		numchars = httpd_get_line(dcb->fd, buf, sizeof(buf));
		if ( (value = strchr(buf, ':'))) {
			*value = '\0';
			value++;
			end = &value[strlen(value) -1];
			*end = '\0';

			if (strncasecmp(buf, "Hostname", 6) == 0) {
				strcpy(client_data->hostname, value);
			}
			if (strncasecmp(buf, "useragent", 9) == 0) {
				strcpy(client_data->useragent, value);
			}
		}
	}

	if (numchars) {
		headers_read = 1;
		memcpy(&client_data->headers_received, &headers_read, sizeof(int));
	}

	/**
	 * Now begins the server reply
	 */

	/* send all the basic headers and close with \r\n */
	httpd_send_headers(dcb, 1);

	/**
	 * ToDO: launch proper content handling based on the requested URI, later REST interface
	 *
	 */

	dcb_printf(dcb, "Welcome to HTTPD Gateway (c) %s\n\n", version_str);

	if (strcmp(url, "/show") == 0) {
		if (strlen(query_string)) {
			if (strcmp(query_string, "dcb") == 0)
				dprintAllDCBs(dcb);
			if (strcmp(query_string, "session") == 0)
				dprintAllSessions(dcb);
		}
	}

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

	return 0;
}
Beispiel #19
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;
}
Beispiel #20
0
/**
 * Start an individual port/protocol pair
 *
 * @param service	The service
 * @param port		The port to start
 * @return		The number of listeners started
 */
static int
serviceStartPort(SERVICE *service, SERV_PROTOCOL *port)
{
int		listeners = 0;
char		config_bind[40];
GWPROTOCOL	*funcs;

        port->listener = dcb_alloc(DCB_ROLE_SERVICE_LISTENER);

        if (port->listener == NULL)
	{
		return 0;
	}
	if (strcmp(port->protocol, "MySQLClient") == 0) {
		int loaded;
		/* Allocate specific data for MySQL users */
		service->users = mysql_users_alloc();
		loaded = load_mysql_users(service);
		/* At service start last update is set to USERS_REFRESH_TIME seconds earlier.
 		 * This way MaxScale could try reloading users' just after startup
 		 */

		service->rate_limit.last=time(NULL) - USERS_REFRESH_TIME;
		service->rate_limit.nloads=1;

		LOGIF(LM, (skygw_log_write(
                        LOGFILE_MESSAGE,
                        "Loaded %d MySQL Users.",
                        loaded)));
	} else {
		/* Generic users table */
		service->users = users_alloc();
	}

	if ((funcs =
             (GWPROTOCOL *)load_module(port->protocol, MODULE_PROTOCOL)) == NULL)
	{
		dcb_free(port->listener);
		port->listener = NULL;
		LOGIF(LE, (skygw_log_write_flush(
                        LOGFILE_ERROR,
			"Error : Unable to load protocol module %s. Listener "
                        "for service %s not started.",
			port->protocol,
                        service->name)));
		return 0;
	}
	memcpy(&(port->listener->func), funcs, sizeof(GWPROTOCOL));
	port->listener->session = NULL;
	if (port->address)
		sprintf(config_bind, "%s:%d", port->address, port->port);
	else
		sprintf(config_bind, "0.0.0.0:%d", port->port);

	if (port->listener->func.listen(port->listener, config_bind)) {
                port->listener->session = session_alloc(service, port->listener);

                if (port->listener->session != NULL) {
                        port->listener->session->state = SESSION_STATE_LISTENER;
                        listeners += 1;
                } else {
                        dcb_close(port->listener);
                }
        } else {
                dcb_close(port->listener);
                
                LOGIF(LE, (skygw_log_write_flush(
                        LOGFILE_ERROR,
			"Error : Unable to start to listen port %d for %s %s.",
			port->port,
                        port->protocol,
                        service->name)));
        }
	return listeners;
}
Beispiel #21
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;
}
Beispiel #22
0
/**
 * Associate a new session with this instance of the filter.
 *
 * Create the file to log to and open it.
 *
 * @param instance	The filter instance data
 * @param session	The session itself
 * @return Session specific data for this session
 */
static void *
newSession(FILTER *instance, SESSION *session)
{
    TEE_INSTANCE *my_instance = (TEE_INSTANCE *) instance;
    TEE_SESSION *my_session;
    char *remote, *userName;

    if (strcmp(my_instance->service->name, session->service->name) == 0)
    {
        MXS_ERROR("%s: Recursive use of tee filter in service.",
                  session->service->name);
        my_session = NULL;
        goto retblock;
    }

    HASHTABLE* ht = hashtable_alloc(100, simple_str_hash, strcmp);
    bool is_loop = detect_loops(my_instance, ht, session->service);
    hashtable_free(ht);

    if (is_loop)
    {
        MXS_ERROR("%s: Recursive use of tee filter in service.",
                  session->service->name);
        my_session = NULL;
        goto retblock;
    }

    if ((my_session = calloc(1, sizeof(TEE_SESSION))) != NULL)
    {
        my_session->active = 1;
        my_session->residual = 0;
        my_session->tee_replybuf = NULL;
        my_session->client_dcb = session->client;
        my_session->instance = my_instance;
        my_session->client_multistatement = false;
        my_session->queue = NULL;
        spinlock_init(&my_session->tee_lock);
        if (my_instance->source &&
            (remote = session_get_remote(session)) != NULL)
        {
            if (strcmp(remote, my_instance->source))
            {
                my_session->active = 0;

                MXS_WARNING("Tee filter is not active.");
            }
        }
        userName = session_getUser(session);

        if (my_instance->userName &&
            userName &&
            strcmp(userName, my_instance->userName))
        {
            my_session->active = 0;

            MXS_WARNING("Tee filter is not active.");
        }

        if (my_session->active)
        {
            DCB* dcb;
            SESSION* ses;
            FILTER_DEF* dummy;
            UPSTREAM* dummy_upstream;

            if ((dcb = dcb_clone(session->client)) == NULL)
            {
                freeSession(instance, (void *) my_session);
                my_session = NULL;

                MXS_ERROR("Creating client DCB for Tee "
                          "filter failed. Terminating session.");

                goto retblock;
            }

            if ((dummy = filter_alloc("tee_dummy", "tee_dummy")) == NULL)
            {
                dcb_close(dcb);
                freeSession(instance, (void *) my_session);
                my_session = NULL;
                MXS_ERROR("tee: Allocating memory for "
                          "dummy filter definition failed."
                          " Terminating session.");

                goto retblock;
            }



            if ((ses = session_alloc(my_instance->service, dcb)) == NULL)
            {
                filter_free(dummy);
                dcb_close(dcb);
                freeSession(instance, (void *) my_session);
                my_session = NULL;
                MXS_ERROR("Creating client session for Tee "
                          "filter failed. Terminating session.");

                goto retblock;
            }

            ss_dassert(ses->ses_is_child);

            dummy->obj = GetModuleObject();
            dummy->filter = NULL;
            my_session->branch_session = ses;
            my_session->branch_dcb = dcb;
            my_session->dummy_filterdef = dummy;

            if ((dummy_upstream = filterUpstream(
                                                 dummy, my_session, &ses->tail)) == NULL)
            {
                filter_free(dummy);
                closeSession(instance, (void*) my_session);
                dcb_close(dcb);
                free(my_session);
                MXS_ERROR("tee: Allocating memory for"
                          "dummy upstream failed."
                          " Terminating session.");

                return NULL;
            }

            ses->tail = *dummy_upstream;
            MySQLProtocol* protocol = (MySQLProtocol*) session->client->protocol;
            my_session->use_ok = protocol->client_capabilities & (1 << 6);
            free(dummy_upstream);
        }
    }
retblock:
    return my_session;
}
Beispiel #23
0
static int
httpd_close(DCB *dcb)
{
	dcb_close(dcb);
	return 0;
}
Beispiel #24
0
/**
 * Start an individual port/protocol pair
 *
 * @param service	The service
 * @param port		The port to start
 * @return		The number of listeners started
 */
static int
serviceStartPort(SERVICE *service, SERV_PROTOCOL *port)
{
int		listeners = 0;
char		config_bind[40];
GWPROTOCOL	*funcs;

        port->listener = dcb_alloc(DCB_ROLE_SERVICE_LISTENER);

        if (port->listener == NULL)
	{
		return 0;
	}
	if (strcmp(port->protocol, "MySQLClient") == 0) {
		int loaded = load_mysql_users(service);
		LOGIF(LM, (skygw_log_write(
                        LOGFILE_MESSAGE,
                        "Loaded %d MySQL Users.",
                        loaded)));
	}

	if ((funcs =
             (GWPROTOCOL *)load_module(port->protocol, MODULE_PROTOCOL)) == NULL)
	{
		dcb_free(port->listener);
		port->listener = NULL;
		LOGIF(LE, (skygw_log_write_flush(
                        LOGFILE_ERROR,
			"Error : Unable to load protocol module %s. Listener "
                        "for service %s not started.",
			port->protocol,
                        service->name)));
		return 0;
	}
	memcpy(&(port->listener->func), funcs, sizeof(GWPROTOCOL));
	port->listener->session = NULL;
	if (port->address)
		sprintf(config_bind, "%s:%d", port->address, port->port);
	else
		sprintf(config_bind, "0.0.0.0:%d", port->port);

	if (port->listener->func.listen(port->listener, config_bind)) {
                port->listener->session = session_alloc(service, port->listener);
                
                if (port->listener->session != NULL) {
                        port->listener->session->state = SESSION_STATE_LISTENER;
                        listeners += 1;
                } else {
                        dcb_close(port->listener);
                }
        } else {
                dcb_close(port->listener);
                
                LOGIF(LE, (skygw_log_write_flush(
                        LOGFILE_ERROR,
			"Error : Unable to start to listen port %d for %s %s.",
			port->port,
                        port->protocol,
                        service->name)));
        }
	return listeners;
}