/*
**	msqld.c	- 
**
**
** Copyright (c) 1993-95  David J. Hughes
** Copyright (c) 1995  Hughes Technologies Pty Ltd
**
** Permission to use, copy, and distribute for non-commercial purposes,
** is hereby granted without fee, providing that the above copyright
** notice appear in all copies and that both the copyright notice and this
** permission notice appear in supporting documentation.
**
** This software is provided "as is" without any expressed or implied warranty.
**
** ID = "$Id:"
**
*/



#include <stdio.h>
#include <fcntl.h>
#include <limits.h>
#include <locale.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>

#ifdef WIN32
#  include <winsock.h>
#endif

#ifndef WIN32
#  include <pwd.h>
#endif

#include <string.h>

#include "version.h"

#ifndef WIN32
#  include <common/config.h>
#endif

#ifdef HAVE_SYS_UN_H
#  include <sys/un.h>
#endif

#include <signal.h>
#include <netdb.h>

#ifdef HAVE_SELECT_H
#  include <select.h>
#endif

#ifdef HAVE_SYS_SELECT_H
#  include <sys/select.h>
#endif

#ifdef HAVE_SETRLIMIT
#  include <sys/resource.h>
#endif

#include "common/site.h"
#include "common/debug.h"

#include "common/portability.h"

#define	_MSQL_SERVER_SOURCE
#include "msql_priv.h"
#include "errmsg.h"
#define	_MSQL_SERVER_PROTO
#include "msql.h"


#ifdef ANSI_ARGS
#  include <stdarg.h>
#else
#  include <varargs.h>
#endif






extern  u_char    *yytext,
		*yyprev;
extern  char    *msqlHomeDir;
extern	int	yylineno;
extern	int	yydebug;

extern	char	errMsg[];

int	curSock,
	IPsock,
	UNIXsock,
	numCons,
	maxCons,
	msyncTimer,
	startTime,
	readOnly;

char	*unixPort,
	confFile[MAXPATHLEN];


cinfo_t	conArray[256];

extern	char	*packet;
char	PROGNAME[] = "msqld",
	BLANK_ARGV[] = "                                                  ";



static char *comTable[] = {
        "???", "Quit", "Init DB", "Query", "DB List", "Table List",
        "Field List", "Create DB", "Drop DB", "Reload ACL",
        "Shutdown", "Index List", "Stats", "Seq Info", "Move DB",
	"Copy DB", "???" };


#ifdef WIN32
RETSIGTYPE sigTrap(int sig);
RETSIGTYPE puntServer(int sig);
RETSIGTYPE puntClient(int sig);
#endif



RETSIGTYPE sigTrap(sig)
	int	sig;
{
	int	clientSock;

#ifdef WIN32
	fprintf(stderr,"\nHit by signal\n\n");
#else
	signal(sig,SIG_IGN);
	fprintf(stderr,"\nHit by a sig %d\n\n",sig);
#endif
	clientSock = 3;
	printf("\n\nForced server shutdown due to bad signal!\n\n");
	while(clientSock < maxCons + CACHE_FDS)
	{
		if (conArray[clientSock].db)
		{
			printf("Forcing close on Socket %d\n",clientSock);
			shutdown(clientSock,2);
			close(clientSock);
		}
		clientSock++;
	}
	shutdown(IPsock,2);
	close(IPsock);
#ifdef HAVE_SYS_UN_H
	shutdown(UNIXsock,2);
	close(UNIXsock);
	unlink(unixPort);
	free(unixPort);
#endif
	printf("\n");
	abort();
}



void mmapSyncer()
{

#ifdef HAVE_MSYNC
	{
		extern	cache_t tableCache[CACHE_SIZE];
		cache_t	*cur;
		int	count;
		mindex_t *curIdx;


		for (count = 0; count < CACHE_SIZE; count++)
		{
			cur = tableCache + count;
			if (cur->age == 0)
			{
				count++;
				continue;
			}
			if (cur->size)
				msync(cur->dataMap,cur->size, 0);
			if (cur->overflowSize)
				msync(cur->overflowMap,cur->overflowSize,0);
			curIdx = cur->indices;
			while(curIdx)
			{
				if (curIdx->idxType == IDX_AVL)
				{
					if(curIdx->tree->mapLen)
						msync(curIdx->tree->mapRegion,
					  	     curIdx->tree->mapLen,0);
				}
				curIdx = curIdx->next;
			}
		}
	}
#else
	sync();
#endif

#if !defined(WIN32) && !defined(OS2)
	signal(SIGALRM, mmapSyncer);
	alarm(msyncTimer);
#endif
}



void sendError(fd,err)
	char	*err;
{
#	ifdef DEBUG
		printf("Send error called\n");
#	endif
	if (err)
		snprintf(packet,PKT_LEN,"-1:%s\n",err);
	else
		snprintf(packet,PKT_LEN,"-1:%s\n",errMsg);
	writePkt(fd);
}

void sendOK(fd)
{
#	ifdef DEBUG
		printf("Send OK called\n");
#	endif
	strcpy(packet,"1:\n");
	writePkt(fd);
}


/****************************************************************************
** 	_initServer
**
**	Purpose	: 
**	Args	: 
**	Returns	: 
**	Notes	: 
*/

void initServer()
{
	int	tcpPort,
		opt;
	struct	sockaddr_in	IPaddr;

#ifdef HAVE_SYS_UN_H
	struct	sockaddr_un	UNIXaddr;
#endif

	/*
	** Create an IP socket
	*/
	if (msqlGetIntConf("system", "remote_access"))
	{
		tcpPort = msqlGetIntConf("general", "tcp_port");
		msqlDebug(MOD_GENERAL,"IP Socket is %d\n",tcpPort);
		IPsock = socket(AF_INET, SOCK_STREAM, 0);
		if (IPsock < 0)
		{
			perror("Can't start server : IP Socket ");
			exit(1);
		}
#		ifdef SO_REUSEADDR
		opt = 1;
		setsockopt(IPsock, SOL_SOCKET, SO_REUSEADDR, (char *)&opt,
			sizeof(int));
#		endif

		bzero(&IPaddr, sizeof(IPaddr));
		IPaddr.sin_family = AF_INET;
		IPaddr.sin_addr.s_addr = htonl(INADDR_ANY);
		IPaddr.sin_port = htons(tcpPort);
		if (bind(IPsock,(struct sockaddr *)&IPaddr,sizeof(IPaddr)) < 0)
		{
			perror("Can't start server : IP Bind ");
			exit(1);
		}
		listen(IPsock,128);
	}
	else
	{
		IPsock = -1;
	}

#	ifdef HAVE_SYS_UN_H
	/*
	** Create the UNIX socket
	*/
	if (msqlGetIntConf("system", "local_access"))
	{
		unixPort = (char *)strdup((char *)msqlGetCharConf("general", 
			"unix_port"));
	
		msqlDebug(MOD_GENERAL,"UNIX Socket is %s\n",unixPort);
		UNIXsock = socket(AF_UNIX, SOCK_STREAM, 0);
		if (UNIXsock < 0)
		{
			perror("Can't start server : UNIX Socket ");
			exit(1);
		}
		bzero(&UNIXaddr, sizeof(UNIXaddr));
		UNIXaddr.sun_family = AF_UNIX;
		strcpy(UNIXaddr.sun_path, unixPort);
		unlink(unixPort);
		if (bind(UNIXsock, (struct sockaddr *)&UNIXaddr, 
			sizeof(UNIXaddr)) < 0)
		{
			perror("Can't start server : UNIX Bind ");
			exit(1);
		}
		listen(UNIXsock,128);
		chmod(unixPort,0777);
	}
	else
#	endif
	{
		UNIXsock = -1;
	}
}



RETSIGTYPE puntServer(sig)
	int	sig;
{
	int	clientSock;

	signal(sig,SIG_IGN);
	clientSock = 3;
	if (sig == -1)
	{
		printf("\n\nNormal Server shutdown!\n\n");
	}
	else
	{
		printf("\n\nServer Aborting!\n\n");
	}
	while(clientSock < maxCons + CACHE_FDS)
	{
		if (conArray[clientSock].db)
		{
			printf("Forcing close on Socket %d\n",clientSock);
			shutdown(clientSock,2);
			close(clientSock);
		}
		clientSock++;
	}
	shutdown(IPsock,2);
	close(IPsock);

#ifdef HAVE_SYS_UN_H
	shutdown(UNIXsock,2);
	close(UNIXsock);
	unlink(unixPort);
	free(unixPort);
#endif
	printf("\n");
	msqlDropCache();

	if (debugSet(MOD_MALLOC))
	{
		fprintf(stderr,"\n\nmalloc() leak detection .....\n");
		checkBlocks(MALLOC_BLK);
	}
	if (debugSet(MOD_MMAP))
	{
		fprintf(stderr,"\n\nmmap() leak detection .....\n");
		checkBlocks(MMAP_BLK);
	}
	printf("\n\nmSQL Daemon Shutdown Complete.\n\n");
	if (sig >= 0)
	{
		exit(1);
	}
	return;
}



void yyerror(s)
        char	*s;
{
 	snprintf(packet,PKT_LEN,"-1:%s at line %d near \"%s %s\"\n", s, 
		yylineno, (char *)yyprev != NULL ?(char *)yyprev:"", 
		(char *)yytext != NULL ?(char *)yytext:"");
	writePkt(curSock);
	msqlClean();
}





static int curComSock;
static fd_set *clientFdSet;

static void setConnectionState(sock, fds)
	int	sock;
	fd_set	*fds;
{
	curComSock = sock;
	clientFdSet = fds;
}


char *getCurUser()
{
	return(strdup(conArray[curComSock].user));
}


RETSIGTYPE puntClient(sig)
	int	sig;
{
	signal(sig, puntClient);
	if (clientFdSet)
	{
		FD_CLR(curComSock, clientFdSet);
		shutdown(curComSock,2);
		close(curComSock);
		if (conArray[curComSock].db)
		{
			safeFree(conArray[curComSock].db);
			safeFree(conArray[curComSock].host);
			safeFree(conArray[curComSock].user);
		}
		conArray[curComSock].db = NULL;
		conArray[curComSock].user = NULL;
		printf("Forced close of client on socket %d due to pipe sig\n",
			curComSock);
		numCons--;
	}
	return;
}



void sendServerStats(sock)
	int	sock;
{
	struct	passwd *pw;
	int	uid,
		loop,
		cTime;
	u_int	curTime = time(NULL);
	char	buf[10];

	snprintf(packet,PKT_LEN, "%s%s\n%s\n%s\n%s\n\n", 
		"Mini SQL Version ", SERVER_VERSION,
		"Copyright (c) 1993-94 David J. Hughes",
		"Copyright (c) 1995-98 Hughes Technologies Pty Ltd.",
		"All rights reserved.");
	writePkt(sock);
	snprintf(packet,PKT_LEN,"Config file      : %s\n",confFile);
	writePkt(sock);
	snprintf(packet,PKT_LEN,"Max connections  : %d\n",maxCons);
	writePkt(sock);
	snprintf(packet,PKT_LEN,"Cur connections  : %d\n",numCons);
	writePkt(sock);

#if !defined(WIN32) && !defined(OS2)
	uid = getuid();
	pw = getpwuid(uid);
	if (pw != NULL)
	{
		snprintf(packet,PKT_LEN,"Running as user  : %s\n", pw->pw_name);
	}
	else
	{
		snprintf(packet,PKT_LEN,"Running as user  : UID %d\n", uid);
	}
	writePkt(sock);
#endif

	strcpy(packet, "\nConnection table :\n");
	writePkt(sock);
	loop = 0;
	strcpy(packet,
	    "  Sock    Username       Hostname        Database    Connect   Idle   Queries\n");
	strcat(packet,
	    " +-----+------------+-----------------+------------+---------+------+--------+\n");
	writePkt(sock);
	while(loop < maxCons)
	{
		if (conArray[loop].user)
		{
			cTime = (int)(curTime - conArray[loop].connectTime)/60;
			snprintf(buf,sizeof(buf),
				"%2dH %2dM",cTime/60, cTime%60);
			snprintf(packet,PKT_LEN,
			 " | %3d | %-10s | %-15s | %-10s | %s | %4d | %6d |\n",
			    loop, conArray[loop].user, 
			    conArray[loop].host?conArray[loop].host:"UNIX Sock",
			    conArray[loop].db ? conArray[loop].db : "No DB",
			    buf,
			    (int)(curTime - conArray[loop].lastQuery)/60,
			    conArray[loop].numQueries);
			writePkt(sock);
		}
		loop++;
	}
	strcpy(packet,
	    " +-----+------------+-----------------+------------+---------+------+--------+\n");
	writePkt(sock);

	strcpy(packet,"\n");
	writePkt(sock);
}

void setupSignals()
{
#ifdef SIGSEGV
	signal(SIGSEGV,sigTrap);
#endif
#ifdef SIGBUS
	signal(SIGBUS,sigTrap);
#endif
#ifdef SIGINT
	signal(SIGINT,puntServer);
#endif
#ifdef SIGQUIT
	signal(SIGQUIT,puntServer);
#endif
#ifdef SIGKILL
	signal(SIGKILL,puntServer);
#endif
#ifdef SIGPIPE
	signal(SIGPIPE,puntClient);
#endif
#ifdef SIGTERM
	signal(SIGTERM,puntServer);
#endif
#ifdef SIGHUP
	signal(SIGHUP,SIG_IGN);
#endif
}



void usage()
{
	printf("Usage :  msql2d [-f ConfFile]\n\n");
}



void main(argc,argv)
	int	argc;
	char	*argv[];
{
	fd_set	readFDs,
		clientFDs;
	int	sock,
		newSock,
		maxSock,
		comSock,
		command,
		opt,
		error,
		errFlag = 0,
		c;
	char	dbname[30],
		*uname,
		*cp,
		*arg,
		*arg2,
		tmpChar,
		path[MAXPATHLEN],
		hostName[256],
		hostAddr[5];

	FILE	*pidFile;
        extern  char *optarg;
#ifdef HAVE_SETRLIMIT
	struct	rlimit limit;
#endif

#ifdef WIN32
	WORD	wVersion;
	WSADATA	wsaData;
#endif
#if !defined(WIN32)
	struct	passwd *pwd;
#endif

	struct	stat sbuf;
	extern	int __MSQL_SERVER;



	__MSQL_SERVER = 1;
	printf("\n\nMini SQL Version %s\n",SERVER_VERSION);
	printf("Copyright (c) 1993-94 David J. Hughes\n");
	printf("Copyright (c) 1995-98 Hughes Technologies Pty Ltd.\n");
	printf("All rights reserved.\n\n");

	/* Try to set the locale but don't bail if it fails (freeBSD) */
	setlocale(LC_ALL, "");
	
	/*
	** Handle the command line args
	*/
	*confFile = 0;
        while((c=getopt(argc,argv,"f:"))!= -1)
        {
                switch(c)
                {
                        case 'f':
                                if (*confFile)
                                        errFlag++;
                                else
                                        strcpy(confFile,optarg);
                                break;
                        case '?':
                                errFlag++;
                                break;
                }
	}
	if (errFlag)
	{
		usage();
		exit(1);
	}

	if (*confFile == 0)
	{
		snprintf(confFile,sizeof(confFile),"%s/msql.conf", INST_DIR);
	}
	printf("\tLoading configuration from '%s'.\n",confFile);
	msqlLoadConfigFile(confFile);

	/*
	** Check out the file descriptor limit
	**
	** Some boxes have broken getrlimit() implementations (or
	** missing RLIMIT_NOFILE) so we try to use sysconf first
	**
	** Watch out for broken BSDI here.  It looks like BSDI returns
	** the kernel MAX_FILES not the per process MAX_FILE in
	** getrlimit() which kinda sucks if you want to use select()!
	*/
#ifdef HAVE_RLIMIT_NOFILE
	getrlimit(RLIMIT_NOFILE,&limit);
	limit.rlim_cur = (limit.rlim_max > 256)? 256 : limit.rlim_max;
	setrlimit(RLIMIT_NOFILE,&limit);
	getrlimit(RLIMIT_NOFILE,&limit);
	maxCons = limit.rlim_cur - CACHE_FDS;
	printf("\tServer process reconfigured to accept %d connections.\n",
		maxCons);
#else
#  ifdef HAVE_SYSCONF
	maxCons = sysconf(_SC_OPEN_MAX) - CACHE_FDS;
	printf("\tServer process reconfigured to accept %d connections.\n",
		maxCons);
#  else
	maxCons = 64 - CACHE_FDS;
	printf("\tDynamic config failed!  Server can accept %d connections.\n",
		maxCons);
#  endif
#endif

	if (maxCons > 254)
		maxCons = 254;

	/*
	** We have to do this now.  If msql is configured to run as nobody
	** then nobody would have to have write perms for the INST_DIR
	** This way we can start as root and then run as the MSQL_USER when
	** we're setup properly
	*/
#ifdef WIN32
	wVersion = MAKEWORD(1,1);
	if (WSAStartup(wVersion, &wsaData) != 0)
	{
		printf("Can't initialise WinSOCK!\n\n");
		exit(1);
	}
	_fmode = _O_BINARY;  /* binary file access is default */
#endif
	initDebug();
	initNet();
	initServer();
	initBackend();
	readOnly = msqlGetIntConf("system","read_only");
	(void)snprintf(path, MAXPATHLEN, "%s", 
		(char *)msqlGetCharConf("general","pid_file"));
	pidFile = fopen(path,"w");
	if (!pidFile)
	{
		perror("Warning : Couldn't open PID file");
	}
	else
	{
		fprintf(pidFile,"%d", (int)getpid());
		fclose(pidFile);
	}

	/*
	** Are we running as the right user?
	*/
#ifndef WIN32
	pwd = getpwuid(geteuid());
	if (!pwd)
	{
		printf("\nError!  No username for our UID (%d)\n\n",
			getuid());
		exit(1);
	}
	uname = (char *)msqlGetCharConf("general","msql_user");
	if (strcmp(uname,pwd->pw_name) != 0)
	{
		pwd = getpwnam(uname);
		if (!pwd)
		{
			printf("\nError!  Unknown username (%s)\n\n", uname);
			exit(1);
		}
		if (setuid(pwd->pw_uid) < 0)
		{
			printf("\nError!  Can't run as user '%s'\n\n",uname);
			exit(1);
		}
	}
	printf("\tServer running as user '%s'.\n",uname);
#endif
	printf("\tServer mode is %s.\n",readOnly?"Read-Only":"Read/Write");

	/*
	** Ensure that the correct user owns the database files
	*/
	snprintf(path,MAXPATHLEN, "%s/msqldb",
		(char *)msqlGetCharConf("general","inst_dir"));
	if (stat(path,&sbuf) < 0)
	{
		printf("\nError!  Can't stat '%s'\n\n",path);
		exit(1);
	}

#if !defined(WIN32) && !defined(OS2)
	if (sbuf.st_uid != geteuid())
	{
		printf("\nError!  '%s' is not owned by '%s'\n\n",path,
			pwd->pw_name);
		exit(1);
	}
#endif

	/*
	** OK, on with the show
	*/

	yytext = NULL;
	umask(0);
	numCons=0;
	maxSock = 0;
	msqlHomeDir = (char *)strdup((char *)msqlGetCharConf("general",
		"inst_dir"));
	chdir(msqlHomeDir);
	cleanTmpDir();

#	ifdef BETA_RELEASE
	timeRemain = (863060000 + 60*24*60*60) - time(NULL);
	if (timeRemain < 0)
	{
		printf("\nThis Beta test release has expired!\n");
		printf("Check out www.Hughes.com.au for a new release!\n\n");
		exit(1);
	}
	else
	{
		printf("\n\tThis Beta test release expires in %d days.\n\n",
			(int)(timeRemain / (24*60*60)));
	}
#	endif

	chmod(path,0700);
	umask(0077);
	setupSignals();
	msqlLoadAcl(1);
	bzero(&clientFDs,sizeof(fd_set));
	bzero(conArray,sizeof(conArray));
	msqlDebug(MOD_GENERAL,
		"miniSQL debug mode.  Waiting for connections.\n");

#if !defined(WIN32) && !defined(OS2)
	msyncTimer = msqlGetIntConf("system","msync_timer");
	if (msyncTimer > 0)
	{
		signal(SIGALRM, mmapSyncer);
		alarm(msyncTimer);
	}
#endif

	while(1)
	{
		bcopy(&clientFDs,&readFDs,sizeof(fd_set));
		if (IPsock >= 0)
			FD_SET(IPsock,&readFDs);
#ifdef HAVE_SYS_UN_H
		if (UNIXsock >= 0)
			FD_SET(UNIXsock,&readFDs);
#endif
		if(select((maxCons+CACHE_FDS),&readFDs,0,0,0) < 0)
			continue;

		/*
		** Is this a new connection request
		*/

		sock = 0;
		if (IPsock >= 0 && FD_ISSET(IPsock,&readFDs))
		{
			sock = IPsock;
		}
#ifdef HAVE_SYS_UN_H
		if (UNIXsock >= 0 && FD_ISSET(UNIXsock,&readFDs))
		{
			sock = UNIXsock;
		}
#endif
		if (sock)
		{
			struct	sockaddr_in 	cAddr;
			struct	sockaddr	dummy;
			size_t	cAddrLen,
				dummyLen;

			bzero(&cAddr, sizeof(cAddr));
			cAddrLen = sizeof(struct sockaddr_in);
			newSock = accept(sock, (struct sockaddr *)&cAddr, 
				&cAddrLen);
			if(newSock < 0)
			{
				perror("Error in accept ");
				continue;
			}
			if (newSock > maxSock)
				maxSock = newSock;
			dummyLen = sizeof(struct sockaddr);
			if (getsockname(newSock,&dummy, &dummyLen) < 0)
			{
				perror("Error on new connection socket");
				continue;
			}
			if (conArray[newSock].db)
			{
				safeFree(conArray[newSock].db);
				safeFree(conArray[newSock].host);
				safeFree(conArray[newSock].user);
				conArray[newSock].db = NULL;
			}

			/*
			** Are we over the connection limit
			*/
			numCons++;
			if (numCons > maxCons)
			{
				numCons--;
				sendError(newSock,CON_COUNT_ERROR);
				shutdown(newSock,2);
				close(newSock);
				continue;
			}


			/*
			** store the connection details
			*/

			msqlDebug(MOD_GENERAL,"New connection received on %d\n",
				newSock);
			error = 0;
			if (sock == IPsock)
			{
				size_t	addrLen;
				struct	hostent *hp, *hp2;

				addrLen = sizeof(struct sockaddr);
				getpeername(newSock, (struct sockaddr *)
					&conArray[newSock].remote, &addrLen);
				addrLen = sizeof(struct sockaddr);
				getsockname(newSock, (struct sockaddr *)
					&conArray[newSock].local, &addrLen);
				if (msqlGetIntConf("system","host_lookup") == 1)
				{
					hp = (struct hostent *)gethostbyaddr(
						(char *)&conArray[newSock].remote.sin_addr,
						sizeof(conArray[newSock].remote.sin_addr),
						AF_INET);
					if (hp)
					{
						char 	*tmp;

						strcpy(hostName,hp->h_name);
						bcopy(hp->h_addr_list[0],
							hostAddr, 4);
						tmp = index(hostName,' ');
						if (tmp)
							*tmp = 0;
						hp2=(struct hostent*)
						    gethostbyname(hostName);
						if (!hp2 ||
							bcmp(hostAddr,
							hp2->h_addr_list[0],
							4)!=0)
						{
						    sendError(newSock,
							BAD_HOST_ERROR);
						    error = 1;
						    shutdown(newSock,2);
						    close(newSock);
						}
					}
				}
				else
				{
					hp = NULL;
				}

				if (!hp)
				{
					struct sockaddr_in *locIP, *remIP;

					locIP = &conArray[newSock].local;
					remIP = &conArray[newSock].remote;

					if (bcmp(&(locIP->sin_addr),
						&(remIP->sin_addr),
						sizeof(u_long))== 0)
					{
						conArray[newSock].host = 
							(char *)
							strdup("localhost");
					}
					else
					{
						conArray[newSock].host = 
							(char *)
							strdup("UNKNOWN_HOST");
						msqlDebug(MOD_GENERAL,
							"Host=UNKNOWN_HOST\n");
					}
				}
				else
				{
					conArray[newSock].host = (char *)
						strdup(hostName);
					msqlDebug(MOD_GENERAL,"Host = %s\n",
						hostName);
				}
			}
			else
			{
				conArray[newSock].host = NULL;
				bzero(&conArray[newSock].local,
					sizeof(struct sockaddr));
				bzero(&conArray[newSock].
					remote,sizeof(struct sockaddr));
				msqlDebug(MOD_GENERAL,"Host = UNIX domain\n");
			}

			conArray[newSock].connectTime = time(NULL);
			conArray[newSock].numQueries = 0;
			setConnectionState(newSock,&clientFDs);

			if (!error)
			{
				opt=1;
				setsockopt(newSock,SOL_SOCKET,SO_KEEPALIVE,
					(char *) &opt, sizeof(opt));
				snprintf(packet, PKT_LEN,
					"0:%d:%s\n",
					PROTOCOL_VERSION,SERVER_VERSION);
				writePkt(newSock);
				if (readPkt(newSock) <=0)
				{
					sendError(newSock,HANDSHAKE_ERROR);
					shutdown(newSock,2);
					close(newSock);
					conArray[newSock].host = NULL;
                                	bzero(&conArray[newSock].local,
                                        	sizeof(struct sockaddr));
                                	bzero(&conArray[newSock].
                                        	remote,sizeof(struct sockaddr));
					numCons--;
				}
				else
				{
					FD_SET(newSock,&clientFDs);
					uname = (char *)strtok(packet,"\n");
					msqlDebug(MOD_GENERAL,"User = %s\n",uname);
					safeFree(conArray[newSock].user);
					conArray[newSock].user = (char *)
						strdup(uname);
					strcpy(packet,"-100:\n");
					writePkt(newSock);
				}
			}
			continue;
		}

	

		/*
		** This must be a command.
		*/	

		comSock = 0;
		while(comSock <= maxSock)
		{
		    if (FD_ISSET(comSock,&readFDs))
		    {
			setConnectionState(comSock,&clientFDs);
			conArray[comSock].lastQuery = time(NULL);
			conArray[comSock].numQueries++;
			if (readPkt(comSock) <= 0)
			{
				msqlDebug(MOD_GENERAL,
					"Command read on sock %d failed!\n",
					comSock);
				command = QUIT;
			}
			else
			{
				command = atoi(packet);
			}
			msqlDebug(MOD_GENERAL,"Command on sock %d = %d (%s)\n",
				comSock, command, comTable[command]);
			switch(command)
			{
			    case INIT_DB:
				cp=(char *)strtok(packet+2,"\n\r");
				if (!cp)
				{
					sendError(comSock, 
						NO_DB_ERROR);
					break;
				}
				strcpy(dbname,cp);
				msqlDebug(MOD_GENERAL,"DBName = %s\n", dbname);
				conArray[comSock].access = msqlCheckAccess(
					dbname, conArray + comSock);
				if(conArray[comSock].access == NO_ACCESS)
				{
					sendError(comSock, 
						ACCESS_DENIED_ERROR);
					break;
				}
				if (msqlInit(dbname) < 0)
				{
					sendError(comSock,NULL);
				}
				else
				{
					sendOK(comSock);
					conArray[comSock].db =
						(char *)strdup(dbname);
					msqlSetDB(dbname);
				}
				break;

			    case QUERY:
				if (!conArray[comSock].db)
				{
					sendError(comSock,NO_DB_ERROR);
					break;
				}
				curSock = comSock;
				cp=(char *)(packet+2);
				arg = (char *)strdup(cp);
				if (debugSet(MOD_QUERY))
					fprintf(stderr,"\n");
				tmpChar = 0;
				if (strlen(arg) > 4096)
				{
					tmpChar = *(arg+4096);
					*(arg+4096) = 0;
				}
				msqlDebug(MOD_QUERY,"Query = %s",arg);
				if (tmpChar)
					*(arg+4096) = tmpChar;
				msqlSetDB(conArray[comSock].db);
				msqlSetPerms(conArray[comSock].access);
				msqlParseQuery(arg,comSock);
				safeFree(arg);
				break;

			    case DB_LIST:
				curSock = comSock;
				msqlServerListDBs(comSock);
				break;

			    case SEQ_INFO:
				if (!conArray[comSock].db)
				{
					sendError(comSock,NO_DB_ERROR);
					break;
				}
				cp = (char *)index(packet,':');
				cp=(char *)strtok(cp+1,
					"\n\r");
				arg = (char *)strdup(cp);
				curSock = comSock;
				msqlServerSequenceInfo(comSock,
					arg,conArray[comSock].db);
				safeFree(arg);
				break;

			    case TABLE_LIST:
				if (!conArray[comSock].db)
				{
					sendError(comSock, NO_DB_ERROR);
					break;
				}
				curSock = comSock;
				msqlServerListTables(comSock,
					conArray[comSock].db);
				break;

			    case FIELD_LIST:
				if (!conArray[comSock].db)
				{
					sendError(comSock,NO_DB_ERROR);
					break;
				}
				cp=(char *)strtok(packet+2,
					"\n\r");
				arg = (char *)strdup(cp);
				curSock = comSock;
				msqlServerListFields(comSock,
					arg,conArray[comSock].db);
				safeFree(arg);
				break;

			    case INDEX_LIST:
				if (!conArray[comSock].db)
				{
					sendError(comSock,NO_DB_ERROR);
					break;
				}
				cp=(char *)strtok(packet+2,
					":\n\r");
				arg = (char *)strdup(cp);
				cp=(char *)strtok(NULL,"\n\r");
				arg2 = (char *)strdup(cp);
				curSock = comSock;
				msqlServerListIndex(comSock,
					arg2,arg,conArray[comSock].db);
				safeFree(arg);
				safeFree(arg2);
				break;

			    case QUIT:
				msqlDebug(MOD_GENERAL,"DB QUIT!\n");
				FD_CLR(comSock,&clientFDs);
				shutdown(comSock,2);
				close(comSock);
				if (conArray[comSock].user)
				{
				   safeFree(conArray[comSock].db);
				   safeFree(conArray[comSock].host);
				   safeFree(conArray[comSock].user);
				}
				conArray[comSock].db = NULL;
				conArray[comSock].user = NULL;
				numCons--;
				break;
		
			    case CREATE_DB:
				if (!msqlCheckLocal(conArray + comSock))
				{
					sendError(comSock,PERM_DENIED_ERROR);
					break;
				}
				cp=(char *)strtok(packet+2,
					"\n\r");
				arg = (char *)strdup(cp);
				msqlServerCreateDB(comSock,arg);
				safeFree(arg);
				break;

			    case COPY_DB:
				if (!msqlCheckLocal(conArray + comSock))
				{
					sendError(comSock,PERM_DENIED_ERROR);
					break;
				}
				cp=(char *)strtok(packet+2, ":\n\r");
				arg = (char *)strdup(cp);
				cp=(char *)strtok(NULL, "\n\r");
				arg2 = (char *)strdup(cp);
				msqlServerCopyDB(comSock,arg, arg2);
				safeFree(arg);
				safeFree(arg2);
				break;

			    case MOVE_DB:
				if (!msqlCheckLocal(conArray + comSock))
				{
					sendError(comSock,PERM_DENIED_ERROR);
					break;
				}
				cp=(char *)strtok(packet+2, ":\n\r");
				arg = (char *)strdup(cp);
				cp=(char *)strtok(NULL, "\n\r");
				arg2 = (char *)strdup(cp);
				msqlServerMoveDB(comSock,arg, arg2);
				safeFree(arg);
				safeFree(arg2);
				break;

			    case DROP_DB:	
				if (!msqlCheckLocal(conArray + comSock))
				{
					sendError(comSock,PERM_DENIED_ERROR);
					break;
				}
				cp=(char *)strtok(packet+2,
					"\n\r");
				arg = (char *)strdup(cp);
				msqlServerDropDB(comSock,arg);
				safeFree(arg);
				break;

			    case RELOAD_ACL:
				if (!msqlCheckLocal(conArray + comSock))
				{
					sendError(comSock,PERM_DENIED_ERROR);
					break;
				}
				(void)strcpy(packet,"-100:\n");
				msqlServerReloadAcls(comSock);
				writePkt(comSock);
				break;

			    case SHUTDOWN:
				if (!msqlCheckLocal(conArray + comSock))
				{
					sendError(comSock,PERM_DENIED_ERROR);
					break;
				}
				strcpy(packet,"-100:\n");
				writePkt(comSock);
				puntServer(-1);
				exit(0);
				break;

			    case SERVER_STATS:
				sendServerStats(comSock);
				strcpy(packet,"-100:\n");
				writePkt(comSock);
				break;

			    default:
				sendError(comSock, UNKNOWN_COM_ERROR);
				break;
			}
			msqlDebug(MOD_GENERAL,"Command Processed!\n");
		    }
		    comSock++;
		}
	}
}
