/*
**	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 <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>
#include <varargs.h>
#include <pwd.h>
#include <string.h>

#include "version.h"
#include <common/config.h>

#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"

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

#include "common/portability.h"




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

extern	char	errMsg[];

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

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", "???" };



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

	signal(sig,SIG_IGN);
	fprintf(stderr,"\nHit by a sig %d\n\n",sig);
	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 sendError(fd,err)
	char	*err;
{
#	ifdef DEBUG
		printf("Send error called\n");
#	endif
	if (err)
		sprintf(packet,"-1:%s\n",err);
	else
		sprintf(packet,"-1:%s\n",errMsg);
	writePkt(fd);
}

void sendOK(fd)
{
#	ifdef DEBUG
		printf("Send OK called\n");
#	endif
	sprintf(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
	*/
	tcpPort = msqlGetIntConf("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

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

#ifdef HAVE_SYS_UN_H
	/*
	** Create the UNIX socket
	*/
	unixPort = (char *)strdup(msqlGetCharConf("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);
	}
	(void)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);
#endif
}



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;
{
 	sprintf(packet,"-1:%s at line %d near \"%s %s\"\n", s, 
		yylineno, yyprev?yyprev:"", yytext?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];

	sprintf(packet,"Mini SQL Version %s\n",SERVER_VERSION);
	strcat(packet,"Copyright (c) 1994-96 Hughes Technologies\n\n");
	writePkt(sock);
	sprintf(packet,"Config file      : %s\n",confFile);
	writePkt(sock);
	sprintf(packet,"Max connections  : %d\n",maxCons);
	writePkt(sock);
	sprintf(packet,"Cur connections  : %d\n",numCons);
	writePkt(sock);

	uid = getuid();
	pw = getpwuid(uid);
	if (pw != NULL)
	{
		sprintf(packet,"Running as user  : %s\n", pw->pw_name);
	}
	else
	{
		sprintf(packet,"Running as user  : UID %d\n", uid);
	}
	writePkt(sock);

	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;
			sprintf(buf,"%2dH %2dM",cTime/60, cTime%60);
			sprintf(packet,
			    " | %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,
		comSock,
		command,
		opt,
		error,
		errFlag = 0,
		c;
	char	dbname[30],
		*uname,
		*cp,
		*arg,
		*arg2,
		path[MAXPATHLEN];
	FILE	*pidFile;
        extern  char *optarg;
	time_t	timeRemain;
	extern  char    *yytext;
	struct	rlimit limit;
	struct	passwd *pwd;
	struct	stat sbuf;



	printf("\n\nMini SQL Version %s\n",SERVER_VERSION);
	printf("Copyright (c) 1994-96 Hughes Technologies\n\n");

	/*
	** 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)
	{
		sprintf(confFile,"%s/msql.conf", INST_DIR);
	}
	printf("Loading 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("Server process reconfigured to accept %d connections.\n",
		maxCons);
#else
#  ifdef HAVE_SYSCONF
	maxCons = sysconf(_SC_OPEN_MAX) - CACHE_FDS;
	printf("Server process reconfigured to accept %d connections.\n",
		maxCons);
#  else
	maxCons = 64 - CACHE_FDS;
	printf("Can't determine Open Max!  Server can accept %d connections.\n",
		maxCons);
#  endif
#endif


	/*
	** Are we running as the right user?
	*/
	pwd = getpwuid(getuid());
	if (!pwd)
	{
		printf("\nError!  No username for our UID (%d)\n\n",
			getuid());
		exit(1);
	}
	uname = (char *)msqlGetCharConf("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("Server running as user '%s'.\n",uname);

	/*
	** Ensure that the correct user owns the database files
	*/
	sprintf(path,"%s/msqldb",msqlGetCharConf("inst_dir"));
	if (stat(path,&sbuf) < 0)
	{
		printf("\nError!  Can't stat '%s'\n\n",path);
		exit(1);
	}
	if (sbuf.st_uid != getuid())
	{
		printf("\nError!  '%s' is not owned by '%s'\n\n",path,
			pwd->pw_name);
		exit(1);
	}

	/*
	** OK, on with the show
	*/
	yytext = NULL;
	umask(0);
	numCons=0;
	msqlHomeDir = (char *)strdup((char *)msqlGetCharConf("inst_dir"));
	chdir(msqlHomeDir);
	cleanTmpDir();

	timeRemain = (851999170 + 91*24*60*60) - time(NULL);
/*	if (timeRemain < 0)
	{
		printf("\nThis Snapshot release has expired!\n");
		printf("Check out www.Hughes.com.au for a new release!\n\n");
		exit(1);
	}
	else
	{
		printf("This snapshot release expires in %d days.\n\n",
			(int)(timeRemain / (24*60*60)));
	}
*/
	printf("This release is suppose to have a expiredate, no new version was released yet\n
		so i _removed_ this time limit\n");
	printf("\nAmiga version was compiled by Mike Erasmus  e-mail: horror@smartnet.co.za, thanx\n
		 goes out to Daniele Peri for HIS help.");
	initDebug();
	initNet();
	initServer();
	initBackend();
	(void)sprintf(path,"%s", (char *)msqlGetCharConf("pid_file"));
	pidFile = fopen(path,"w");
	if (!pidFile)
	{
		perror("Couldn't open PID file");
	}
	else
	{
		fprintf(pidFile,"%d", (int)getpid());
		fclose(pidFile);
	}
	chmod(path,0644);
	umask(0);
	setupSignals();
	msqlLoadAcl(1);
	(void)bzero(&clientFDs,sizeof(fd_set));
	(void)bzero(conArray,sizeof(conArray));
	msqlDebug(MOD_GENERAL,"miniSQL debug mode.  Waiting for connections.\n");
	while(1)
	{
		(void)bcopy(&clientFDs,&readFDs,sizeof(fd_set));
		FD_SET(IPsock,&readFDs);
#ifdef HAVE_SYS_UN_H
		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 (FD_ISSET(IPsock,&readFDs))
		{
			sock = IPsock;
		}
#ifdef HAVE_SYS_UN_H
		if (FD_ISSET(UNIXsock,&readFDs))
		{
			sock = UNIXsock;
		}
#endif
		if (sock)
		{
			struct	sockaddr_in 	cAddr;
			struct	sockaddr	dummy;
			int	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;
			}
			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)
			{
				int	addrLen;
				struct	hostent *hp;

				addrLen = sizeof(struct sockaddr);
				getpeername(newSock, (struct sockaddr *)
					&conArray[newSock].remote, &addrLen);
				addrLen = sizeof(struct sockaddr);
				getsockname(newSock, (struct sockaddr *)
					&conArray[newSock].local, &addrLen);
				hp = (struct hostent *)gethostbyaddr(
				    (char *)&conArray[newSock].remote.sin_addr,
				    sizeof(conArray[newSock].remote.sin_addr),
				    AF_INET);
				if (!hp)
				{
					sendError(newSock, BAD_HOST_ERROR);
					error = 1;
					shutdown(newSock,2);
					close(newSock);
					
				}
				else
				{
					conArray[newSock].host = (char *)
						strdup(hp->h_name);
					msqlDebug(MOD_GENERAL,"Host = %s\n",
						hp->h_name);
				}
			}
			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));
				sprintf(packet,
					"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));

				}
				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);
					sprintf(packet,"-100:\n");
					writePkt(newSock);
				}
			}
			continue;
		}

	

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

		comSock = 3;
		while(comSock < maxCons + CACHE_FDS)
		{
		    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");
				msqlDebug(MOD_QUERY,"Query = %s",arg);
				msqlSetDB(conArray[comSock].db);
				msqlSetPerms(conArray[comSock].access);
				msqlParseQuery(arg,comSock);
				safeFree(arg);
				break;

			    case DB_LIST:
				curSock = comSock;
				msqlServerListDBs(comSock);
				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 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)sprintf(packet,"-100:\n");
				msqlServerReloadAcls(comSock);
				writePkt(comSock);
				break;

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

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

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