725 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			725 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * This file is distributed as part of MaxScale.  It is free
 | 
						|
 * software: you can redistribute it and/or modify it under the terms of the
 | 
						|
 * GNU General Public License as published by the Free Software Foundation,
 | 
						|
 * version 2.
 | 
						|
 *
 | 
						|
 * This program is distributed in the hope that it will be useful, but WITHOUT
 | 
						|
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
						|
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
						|
 * details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU General Public License along with
 | 
						|
 * this program; if not, write to the Free Software Foundation, Inc., 51
 | 
						|
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 | 
						|
 *
 | 
						|
 * Copyright MariaDB Corporation Ab 2014
 | 
						|
 */
 | 
						|
 | 
						|
/**
 | 
						|
 * @file blr_file.c - contains code for the router binlog file management
 | 
						|
 *
 | 
						|
 *
 | 
						|
 * @verbatim
 | 
						|
 * Revision History
 | 
						|
 *
 | 
						|
 * Date		Who		Description
 | 
						|
 * 14/04/2014	Mark Riddoch		Initial implementation
 | 
						|
 *
 | 
						|
 * @endverbatim
 | 
						|
 */
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <errno.h>
 | 
						|
#include <string.h>
 | 
						|
#include <sys/types.h>
 | 
						|
#include <sys/stat.h>
 | 
						|
#include <dirent.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <service.h>
 | 
						|
#include <server.h>
 | 
						|
#include <router.h>
 | 
						|
#include <atomic.h>
 | 
						|
#include <spinlock.h>
 | 
						|
#include <blr.h>
 | 
						|
#include <dcb.h>
 | 
						|
#include <spinlock.h>
 | 
						|
 | 
						|
#include <skygw_types.h>
 | 
						|
#include <skygw_utils.h>
 | 
						|
#include <log_manager.h>
 | 
						|
 | 
						|
extern int lm_enabled_logfiles_bitmask;
 | 
						|
extern size_t         log_ses_count[];
 | 
						|
extern __thread log_info_t tls_log_info;
 | 
						|
 | 
						|
 | 
						|
static int  blr_file_create(ROUTER_INSTANCE *router, char *file);
 | 
						|
static void blr_file_append(ROUTER_INSTANCE *router, char *file);
 | 
						|
static uint32_t extract_field(uint8_t *src, int bits);
 | 
						|
static void blr_log_header(logfile_id_t file, char *msg, uint8_t *ptr);
 | 
						|
 | 
						|
/**
 | 
						|
 * Initialise the binlog file for this instance. MaxScale will look
 | 
						|
 * for all the binlogs that it has on local disk, determine the next
 | 
						|
 * binlog to use and initialise it for writing, determining the 
 | 
						|
 * next record to be fetched from the real master.
 | 
						|
 *
 | 
						|
 * @param router	The router instance this defines the master for this replication chain
 | 
						|
 */
 | 
						|
int
 | 
						|
blr_file_init(ROUTER_INSTANCE *router)
 | 
						|
{
 | 
						|
char		*ptr, path[PATH_MAX], filename[PATH_MAX];
 | 
						|
int		file_found, n = 1;
 | 
						|
int		root_len, i;
 | 
						|
DIR		*dirp;
 | 
						|
struct dirent	*dp;
 | 
						|
 | 
						|
	if (router->binlogdir == NULL)
 | 
						|
	{
 | 
						|
		strcpy(path, "/usr/local/mariadb-maxscale");
 | 
						|
		if ((ptr = getenv("MAXSCALE_HOME")) != NULL)
 | 
						|
		{
 | 
						|
			strncpy(path, ptr,PATH_MAX);
 | 
						|
		}
 | 
						|
		strncat(path, "/",PATH_MAX);
 | 
						|
		strncat(path, router->service->name,PATH_MAX);
 | 
						|
 | 
						|
		if (access(path, R_OK) == -1)
 | 
						|
			mkdir(path, 0777);
 | 
						|
 | 
						|
		router->binlogdir = strdup(path);
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		strncpy(path, router->binlogdir, PATH_MAX);
 | 
						|
	}
 | 
						|
	if (access(router->binlogdir, R_OK) == -1)
 | 
						|
	{
 | 
						|
		LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
 | 
						|
			"%s: Unable to read the binlog directory %s.",
 | 
						|
					router->service->name, router->binlogdir)));
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	/* First try to find a binlog file number by reading the directory */
 | 
						|
	root_len = strlen(router->fileroot);
 | 
						|
	if ((dirp = opendir(path)) == NULL)
 | 
						|
	{
 | 
						|
		LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
 | 
						|
			"%s: Unable to read the binlog directory %s, %s.",
 | 
						|
				router->service->name, router->binlogdir,
 | 
						|
				strerror(errno))));
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	while ((dp = readdir(dirp)) != NULL)
 | 
						|
	{
 | 
						|
		if (strncmp(dp->d_name, router->fileroot, root_len) == 0)
 | 
						|
		{
 | 
						|
			i = atoi(dp->d_name + root_len + 1);
 | 
						|
			if (i > n)
 | 
						|
				n = i;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	closedir(dirp);
 | 
						|
 | 
						|
 | 
						|
	file_found = 0;
 | 
						|
	do {
 | 
						|
		snprintf(filename,PATH_MAX, "%s/" BINLOG_NAMEFMT, path, router->fileroot, n);
 | 
						|
		if (access(filename, R_OK) != -1)
 | 
						|
		{
 | 
						|
			file_found  = 1;
 | 
						|
			n++;
 | 
						|
		}
 | 
						|
		else
 | 
						|
			file_found = 0;
 | 
						|
	} while (file_found);
 | 
						|
	n--;
 | 
						|
 | 
						|
	if (n == 0)		// No binlog files found
 | 
						|
	{
 | 
						|
		if (router->initbinlog)
 | 
						|
			snprintf(filename,PATH_MAX, BINLOG_NAMEFMT, router->fileroot,
 | 
						|
						router->initbinlog);
 | 
						|
		else
 | 
						|
			snprintf(filename,PATH_MAX, BINLOG_NAMEFMT, router->fileroot, 1);
 | 
						|
		if (! blr_file_create(router, filename))
 | 
						|
			return 0;
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		snprintf(filename,PATH_MAX, BINLOG_NAMEFMT, router->fileroot, n);
 | 
						|
		blr_file_append(router, filename);
 | 
						|
	}
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
blr_file_rotate(ROUTER_INSTANCE *router, char *file, uint64_t pos)
 | 
						|
{
 | 
						|
	return blr_file_create(router, file);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 * Create a new binlog file for the router to use.
 | 
						|
 *
 | 
						|
 * @param router	The router instance
 | 
						|
 * @param file		The binlog file name
 | 
						|
 * @return		Non-zero if the fie creation succeeded
 | 
						|
 */
 | 
						|
static int
 | 
						|
blr_file_create(ROUTER_INSTANCE *router, char *file)
 | 
						|
{
 | 
						|
char		path[1024];
 | 
						|
int		fd;
 | 
						|
unsigned char	magic[] = BINLOG_MAGIC;
 | 
						|
 | 
						|
	strcpy(path, router->binlogdir);
 | 
						|
	strcat(path, "/");
 | 
						|
	strcat(path, file);
 | 
						|
 | 
						|
	if ((fd = open(path, O_RDWR|O_CREAT, 0666)) != -1)
 | 
						|
	{
 | 
						|
		write(fd, magic, 4);
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
 | 
						|
			"%s: Failed to create binlog file %s, %s.",
 | 
						|
				router->service->name, path, strerror(errno))));
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	fsync(fd);
 | 
						|
	close(router->binlog_fd);
 | 
						|
	spinlock_acquire(&router->binlog_lock);
 | 
						|
	strncpy(router->binlog_name, file,BINLOG_FNAMELEN);
 | 
						|
	router->binlog_position = 4;			/* Initial position after the magic number */
 | 
						|
	spinlock_release(&router->binlog_lock);
 | 
						|
	router->binlog_fd = fd;
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 * Prepare an existing binlog file to be appened to.
 | 
						|
 *
 | 
						|
 * @param router	The router instance
 | 
						|
 * @param file		The binlog file name
 | 
						|
 */
 | 
						|
static void
 | 
						|
blr_file_append(ROUTER_INSTANCE *router, char *file)
 | 
						|
{
 | 
						|
char		path[1024];
 | 
						|
int		fd;
 | 
						|
 | 
						|
	strcpy(path, router->binlogdir);
 | 
						|
	strcat(path, "/");
 | 
						|
	strcat(path, file);
 | 
						|
 | 
						|
	if ((fd = open(path, O_RDWR|O_APPEND, 0666)) == -1)
 | 
						|
	{
 | 
						|
		LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
 | 
						|
			"Failed to open binlog file %s for append.",
 | 
						|
				path)));
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	fsync(fd);
 | 
						|
	close(router->binlog_fd);
 | 
						|
	spinlock_acquire(&router->binlog_lock);
 | 
						|
	strncpy(router->binlog_name, file,BINLOG_FNAMELEN);
 | 
						|
	router->binlog_position = lseek(fd, 0L, SEEK_END);
 | 
						|
	spinlock_release(&router->binlog_lock);
 | 
						|
	router->binlog_fd = fd;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Write a binlog entry to disk.
 | 
						|
 *
 | 
						|
 * @param router	The router instance
 | 
						|
 * @param buf		The binlog record
 | 
						|
 * @param len		The length of the binlog record
 | 
						|
 * @return 		Return the number of bytes written
 | 
						|
 */
 | 
						|
int
 | 
						|
blr_write_binlog_record(ROUTER_INSTANCE *router, REP_HEADER *hdr, uint8_t *buf)
 | 
						|
{
 | 
						|
int	n;
 | 
						|
 | 
						|
	if ((n = pwrite(router->binlog_fd, buf, hdr->event_size,
 | 
						|
				hdr->next_pos - hdr->event_size)) != hdr->event_size)
 | 
						|
	{
 | 
						|
		LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
 | 
						|
			"%s: Failed to write binlog record at %d of %s, %s. "
 | 
						|
			"Truncating to previous record.",
 | 
						|
			router->service->name, hdr->next_pos - hdr->event_size,
 | 
						|
			router->binlog_name,
 | 
						|
			strerror(errno))));
 | 
						|
		/* Remove any partual event that was written */
 | 
						|
		ftruncate(router->binlog_fd, hdr->next_pos - hdr->event_size);
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	spinlock_acquire(&router->binlog_lock);
 | 
						|
	router->binlog_position = hdr->next_pos;
 | 
						|
	router->last_written = hdr->next_pos - hdr->event_size;
 | 
						|
	spinlock_release(&router->binlog_lock);
 | 
						|
	return n;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Flush the content of the binlog file to disk.
 | 
						|
 *
 | 
						|
 * @param	router		The binlog router
 | 
						|
 */
 | 
						|
void
 | 
						|
blr_file_flush(ROUTER_INSTANCE *router)
 | 
						|
{
 | 
						|
	fsync(router->binlog_fd);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Open a binlog file for reading binlog records
 | 
						|
 *
 | 
						|
 * @param router	The router instance
 | 
						|
 * @param binlog	The binlog filename
 | 
						|
 * @return a binlog file record
 | 
						|
 */
 | 
						|
BLFILE *
 | 
						|
blr_open_binlog(ROUTER_INSTANCE *router, char *binlog)
 | 
						|
{
 | 
						|
char		path[1025];
 | 
						|
BLFILE		*file;
 | 
						|
 | 
						|
	spinlock_acquire(&router->fileslock);
 | 
						|
	file = router->files;
 | 
						|
	while (file && strcmp(file->binlogname, binlog) != 0)
 | 
						|
		file = file->next;
 | 
						|
 | 
						|
	if (file)
 | 
						|
	{
 | 
						|
		file->refcnt++;
 | 
						|
		spinlock_release(&router->fileslock);
 | 
						|
		return file;
 | 
						|
	}
 | 
						|
 | 
						|
	if ((file = (BLFILE *)malloc(sizeof(BLFILE))) == NULL)
 | 
						|
	{
 | 
						|
		spinlock_release(&router->fileslock);
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
	strncpy(file->binlogname, binlog,BINLOG_FNAMELEN+1);
 | 
						|
	file->refcnt = 1;
 | 
						|
	file->cache = 0;
 | 
						|
	spinlock_init(&file->lock);
 | 
						|
 | 
						|
	strncpy(path, router->binlogdir,1024);
 | 
						|
	strncat(path, "/",1024);
 | 
						|
	strncat(path, binlog,1024);
 | 
						|
 | 
						|
	if ((file->fd = open(path, O_RDONLY, 0666)) == -1)
 | 
						|
	{
 | 
						|
		LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
 | 
						|
			"Failed to open binlog file %s", path)));
 | 
						|
		free(file);
 | 
						|
		spinlock_release(&router->fileslock);
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	file->next = router->files;
 | 
						|
	router->files = file;
 | 
						|
	spinlock_release(&router->fileslock);
 | 
						|
 | 
						|
	return file;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Read a replication event into a GWBUF structure.
 | 
						|
 *
 | 
						|
 * @param router	The router instance
 | 
						|
 * @param file		File record
 | 
						|
 * @param pos		Position of binlog record to read
 | 
						|
 * @param hdr		Binlog header to populate
 | 
						|
 * @return		The binlog record wrapped in a GWBUF structure
 | 
						|
 */
 | 
						|
GWBUF *
 | 
						|
blr_read_binlog(ROUTER_INSTANCE *router, BLFILE *file, unsigned int pos, REP_HEADER *hdr)
 | 
						|
{
 | 
						|
uint8_t		hdbuf[19];
 | 
						|
GWBUF		*result;
 | 
						|
unsigned char	*data;
 | 
						|
int		n;
 | 
						|
unsigned long	filelen = 0;
 | 
						|
struct	stat	statb;
 | 
						|
 | 
						|
	if (!file)
 | 
						|
	{
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
	if (fstat(file->fd, &statb) == 0)
 | 
						|
		filelen = statb.st_size;
 | 
						|
	if (pos >= filelen)
 | 
						|
	{
 | 
						|
		LOGIF(LD, (skygw_log_write(LOGFILE_ERROR,
 | 
						|
			"Attempting to read off the end of the binlog file %s, "
 | 
						|
			"event at %lu.", file->binlogname, pos)));
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	if (strcmp(router->binlog_name, file->binlogname) == 0 &&
 | 
						|
			pos >= router->binlog_position)
 | 
						|
	{
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
		
 | 
						|
 | 
						|
	/* Read the header information from the file */
 | 
						|
	if ((n = pread(file->fd, hdbuf, 19, pos)) != 19)
 | 
						|
	{
 | 
						|
		switch (n)
 | 
						|
		{
 | 
						|
		case 0:
 | 
						|
			LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG,
 | 
						|
				"Reached end of binlog file at %d.",
 | 
						|
					pos)));
 | 
						|
			break;
 | 
						|
		case -1:
 | 
						|
			LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
 | 
						|
				"Failed to read binlog file %s at position %d"
 | 
						|
				" (%s).", file->binlogname,
 | 
						|
						pos, strerror(errno))));
 | 
						|
			if (errno == EBADF)
 | 
						|
				LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
 | 
						|
					"Bad file descriptor in read binlog for file %s"
 | 
						|
					", reference count is %d, descriptor %d.",
 | 
						|
						file->binlogname, file->refcnt, file->fd)));
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
 | 
						|
				"Short read when reading the header. "
 | 
						|
				"Expected 19 bytes but got %d bytes. "
 | 
						|
				"Binlog file is %s, position %d",
 | 
						|
				n, file->binlogname, pos)));
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
	hdr->timestamp = EXTRACT32(hdbuf);
 | 
						|
	hdr->event_type = hdbuf[4];
 | 
						|
	hdr->serverid = EXTRACT32(&hdbuf[5]);
 | 
						|
	hdr->event_size = extract_field(&hdbuf[9], 32);
 | 
						|
	hdr->next_pos = EXTRACT32(&hdbuf[13]);
 | 
						|
	hdr->flags = EXTRACT16(&hdbuf[17]);
 | 
						|
 | 
						|
	if (hdr->event_type > MAX_EVENT_TYPE)
 | 
						|
	{
 | 
						|
		LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
 | 
						|
				"Invalid event type 0x%x. "
 | 
						|
				"Binlog file is %s, position %d",
 | 
						|
				hdr->event_type,
 | 
						|
				file->binlogname, pos)));
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	if (hdr->next_pos < pos && hdr->event_type != ROTATE_EVENT)
 | 
						|
	{
 | 
						|
		LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
 | 
						|
			"Next position in header appears to be incorrect "
 | 
						|
			"rereading event header at pos %ul in file %s, "
 | 
						|
			"file size is %ul. Master will write %ul in %s next.",
 | 
						|
			pos, file->binlogname, filelen, router->binlog_position,
 | 
						|
			router->binlog_name)));
 | 
						|
		if ((n = pread(file->fd, hdbuf, 19, pos)) != 19)
 | 
						|
		{
 | 
						|
			switch (n)
 | 
						|
			{
 | 
						|
			case 0:
 | 
						|
				LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG,
 | 
						|
					"Reached end of binlog file at %d.",
 | 
						|
						pos)));
 | 
						|
				break;
 | 
						|
			case -1:
 | 
						|
				LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
 | 
						|
					"Failed to read binlog file %s at position %d"
 | 
						|
					" (%s).", file->binlogname,
 | 
						|
							pos, strerror(errno))));
 | 
						|
				if (errno == EBADF)
 | 
						|
					LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
 | 
						|
						"Bad file descriptor in read binlog for file %s"
 | 
						|
						", reference count is %d, descriptor %d.",
 | 
						|
							file->binlogname, file->refcnt, file->fd)));
 | 
						|
				break;
 | 
						|
			default:
 | 
						|
				LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
 | 
						|
					"Short read when reading the header. "
 | 
						|
					"Expected 19 bytes but got %d bytes. "
 | 
						|
					"Binlog file is %s, position %d",
 | 
						|
					file->binlogname, pos, n)));
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			return NULL;
 | 
						|
		}
 | 
						|
		hdr->timestamp = EXTRACT32(hdbuf);
 | 
						|
		hdr->event_type = hdbuf[4];
 | 
						|
		hdr->serverid = EXTRACT32(&hdbuf[5]);
 | 
						|
		hdr->event_size = extract_field(&hdbuf[9], 32);
 | 
						|
		hdr->next_pos = EXTRACT32(&hdbuf[13]);
 | 
						|
		hdr->flags = EXTRACT16(&hdbuf[17]);
 | 
						|
 | 
						|
		if (hdr->next_pos < pos && hdr->event_type != ROTATE_EVENT)
 | 
						|
		{
 | 
						|
			LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
 | 
						|
				"Next position still incorrect after "
 | 
						|
				"rereading")));
 | 
						|
			return NULL;
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
 | 
						|
				"Next position corrected by "
 | 
						|
				"rereading")));
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if ((result = gwbuf_alloc(hdr->event_size)) == NULL)
 | 
						|
	{
 | 
						|
		LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
 | 
						|
			"Failed to allocate memory for binlog entry, "
 | 
						|
                        "size %d at %d.",
 | 
						|
                        hdr->event_size, pos)));
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
	data = GWBUF_DATA(result);
 | 
						|
	memcpy(data, hdbuf, 19);	// Copy the header in
 | 
						|
	if ((n = pread(file->fd, &data[19], hdr->event_size - 19, pos + 19))
 | 
						|
			!= hdr->event_size - 19)	// Read the balance
 | 
						|
	{
 | 
						|
		if (n == -1)
 | 
						|
		{
 | 
						|
			LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
 | 
						|
				"Error reading the event at %ld in %s. "
 | 
						|
				"%s, expected %d bytes.",
 | 
						|
				pos, file->binlogname, 
 | 
						|
				strerror(errno), hdr->event_size - 19)));
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
 | 
						|
				"Short read when reading the event at %ld in %s. "
 | 
						|
				"Expected %d bytes got %d bytes.",
 | 
						|
				pos, file->binlogname, hdr->event_size - 19, n)));
 | 
						|
			if (filelen != 0 && filelen - pos < hdr->event_size)
 | 
						|
			{
 | 
						|
				LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
 | 
						|
					"Binlog event is close to the end of the binlog file, "
 | 
						|
					"current file size is %u.",
 | 
						|
					filelen)));
 | 
						|
			}
 | 
						|
			blr_log_header(LOGFILE_ERROR, "Possible malformed event header", hdbuf);
 | 
						|
		}
 | 
						|
		gwbuf_consume(result, hdr->event_size);
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Close a binlog file that has been opened to read binlog records
 | 
						|
 *
 | 
						|
 * The open binlog files are shared between multiple slaves that are
 | 
						|
 * reading the same binlog file.
 | 
						|
 *
 | 
						|
 * @param router	The router instance
 | 
						|
 * @param file		The file to close
 | 
						|
 */
 | 
						|
void
 | 
						|
blr_close_binlog(ROUTER_INSTANCE *router, BLFILE *file)
 | 
						|
{
 | 
						|
	spinlock_acquire(&file->lock);
 | 
						|
	file->refcnt--;
 | 
						|
	if (file->refcnt == 0)
 | 
						|
	{
 | 
						|
		spinlock_acquire(&router->fileslock);
 | 
						|
		if (router->files == file)
 | 
						|
			router->files = file->next;
 | 
						|
		else
 | 
						|
		{
 | 
						|
			BLFILE	*ptr = router->files;
 | 
						|
			while (ptr && ptr->next != file)
 | 
						|
				ptr = ptr->next;
 | 
						|
			if (ptr)
 | 
						|
				ptr->next = file->next;
 | 
						|
		}
 | 
						|
		spinlock_release(&router->fileslock);
 | 
						|
 | 
						|
		close(file->fd);
 | 
						|
		file->fd = -1;
 | 
						|
	}
 | 
						|
	spinlock_release(&file->lock);
 | 
						|
	if (file->refcnt == 0)
 | 
						|
		free(file);
 | 
						|
}
 | 
						|
 | 
						|
/** 
 | 
						|
 * Extract a numeric field from a packet of the specified number of bits
 | 
						|
 *
 | 
						|
 * @param src	The raw packet source
 | 
						|
 * @param birs	The number of bits to extract (multiple of 8)
 | 
						|
 */
 | 
						|
static uint32_t
 | 
						|
extract_field(uint8_t *src, int bits)
 | 
						|
{
 | 
						|
uint32_t	rval = 0, shift = 0;
 | 
						|
 | 
						|
	while (bits > 0)
 | 
						|
	{
 | 
						|
		rval |= (*src++) << shift;
 | 
						|
		shift += 8;
 | 
						|
		bits -= 8;
 | 
						|
	}
 | 
						|
	return rval;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Log the event header of  binlog event
 | 
						|
 *
 | 
						|
 * @param	file	The log file into which to write the entry
 | 
						|
 * @param 	msg	A message strign to preceed the header with
 | 
						|
 * @param	ptr	The event header raw data
 | 
						|
 */
 | 
						|
static void
 | 
						|
blr_log_header(logfile_id_t file, char *msg, uint8_t *ptr)
 | 
						|
{
 | 
						|
char	buf[400], *bufp;
 | 
						|
int	i;
 | 
						|
 | 
						|
	bufp = buf;
 | 
						|
	bufp += sprintf(bufp, "%s: ", msg);
 | 
						|
	for (i = 0; i < 19; i++)
 | 
						|
		bufp += sprintf(bufp, "0x%02x ", ptr[i]);
 | 
						|
	skygw_log_write_flush(file, "%s", buf);
 | 
						|
	
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Return the size of the current binlog file
 | 
						|
 *
 | 
						|
 * @param file	The binlog file
 | 
						|
 * @return	The current size of the binlog file
 | 
						|
 */
 | 
						|
unsigned long
 | 
						|
blr_file_size(BLFILE *file)
 | 
						|
{
 | 
						|
struct	stat	statb;
 | 
						|
 | 
						|
	if (fstat(file->fd, &statb) == 0)
 | 
						|
		return statb.st_size;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 * Write the response packet to a cache file so that MaxScale can respond
 | 
						|
 * even if there is no master running when MaxScale starts.
 | 
						|
 *
 | 
						|
 * @param router	The instance of the router
 | 
						|
 * @param response	The name of the response, used to name the cached file
 | 
						|
 * @param buf		The buffer to written to the cache
 | 
						|
 */
 | 
						|
void
 | 
						|
blr_cache_response(ROUTER_INSTANCE *router, char *response, GWBUF *buf)
 | 
						|
{
 | 
						|
char	path[4097], *ptr;
 | 
						|
int	fd;
 | 
						|
 | 
						|
	strcpy(path, "/usr/local/mariadb-maxscale");
 | 
						|
	if ((ptr = getenv("MAXSCALE_HOME")) != NULL)
 | 
						|
	{
 | 
						|
		strncpy(path, ptr, 4096);
 | 
						|
	}
 | 
						|
	strncat(path, "/", 4096);
 | 
						|
	strncat(path, router->service->name, 4096);
 | 
						|
 | 
						|
	if (access(path, R_OK) == -1)
 | 
						|
		mkdir(path, 0777);
 | 
						|
	strncat(path, "/.cache", 4096);
 | 
						|
	if (access(path, R_OK) == -1)
 | 
						|
		mkdir(path, 0777);
 | 
						|
	strncat(path, "/", 4096);
 | 
						|
	strncat(path, response, 4096);
 | 
						|
 | 
						|
	if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0666)) == -1)
 | 
						|
		return;
 | 
						|
	write(fd, GWBUF_DATA(buf), GWBUF_LENGTH(buf));
 | 
						|
	close(fd);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Read a cached copy of a master response message. This allows
 | 
						|
 * the router to start and serve any binlogs it already has on disk
 | 
						|
 * if the master is not available.
 | 
						|
 *
 | 
						|
 * @param router	The router instance structure
 | 
						|
 * @param response	The name of the response
 | 
						|
 * @return A pointer to a GWBUF structure
 | 
						|
 */
 | 
						|
GWBUF *
 | 
						|
blr_cache_read_response(ROUTER_INSTANCE *router, char *response)
 | 
						|
{
 | 
						|
struct	stat	statb;
 | 
						|
char	path[4097], *ptr;
 | 
						|
int	fd;
 | 
						|
GWBUF	*buf;
 | 
						|
 | 
						|
	strcpy(path, "/usr/local/mariadb-maxscale");
 | 
						|
	if ((ptr = getenv("MAXSCALE_HOME")) != NULL)
 | 
						|
	{
 | 
						|
		strncpy(path, ptr, 4096);
 | 
						|
	}
 | 
						|
	strncat(path, "/", 4096);
 | 
						|
	strncat(path, router->service->name, 4096);
 | 
						|
	strncat(path, "/.cache/", 4096);
 | 
						|
	strncat(path, response, 4096);
 | 
						|
 | 
						|
	if ((fd = open(path, O_RDONLY)) == -1)
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	if (fstat(fd, &statb) != 0)
 | 
						|
	{
 | 
						|
		close(fd);
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
	if ((buf = gwbuf_alloc(statb.st_size)) == NULL)
 | 
						|
	{
 | 
						|
		close(fd);
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
	read(fd, GWBUF_DATA(buf), statb.st_size);
 | 
						|
	close(fd);
 | 
						|
	return buf;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Does the next binlog file in the sequence for the slave exist.
 | 
						|
 *
 | 
						|
 * @param router	The router instance
 | 
						|
 * @param slave		The slave in question
 | 
						|
 * @retuen 		0 if the next file does not exist
 | 
						|
 */
 | 
						|
int
 | 
						|
blr_file_next_exists(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave)
 | 
						|
{
 | 
						|
char	*sptr, buf[80], bigbuf[4096];
 | 
						|
int	filenum;
 | 
						|
 | 
						|
	if ((sptr = strrchr(slave->binlogfile, '.')) == NULL)
 | 
						|
		return 0;
 | 
						|
	filenum = atoi(sptr + 1);
 | 
						|
	sprintf(buf, BINLOG_NAMEFMT, router->fileroot, filenum + 1);
 | 
						|
	sprintf(bigbuf, "%s/%s", router->binlogdir, buf);
 | 
						|
	if (access(bigbuf, R_OK) == -1)
 | 
						|
		return 0;
 | 
						|
	return 1;
 | 
						|
}
 |