Pre-emptive removal of gamedata updater pending rewrite.
This commit is contained in:
parent
d2ce50f989
commit
ccc59ecdaf
@ -104,16 +104,6 @@
|
||||
*/
|
||||
"ForceRestartAfterUpdate" "no"
|
||||
|
||||
/**
|
||||
* Sets the server to connect to for auotmatic gamedata updates.
|
||||
*/
|
||||
"AutoUpdateServer" "smupdate.alliedmods.net"
|
||||
|
||||
/**
|
||||
* Sets the port to connect to on the AutoUpdateServer server
|
||||
*/
|
||||
"AutoUpdatePort" "6500"
|
||||
|
||||
/**
|
||||
* Whether to show debug spew.
|
||||
* Currently this will log details about the gamedata updating process.
|
||||
|
@ -42,7 +42,6 @@
|
||||
#include "LibrarySys.h"
|
||||
#include "HandleSys.h"
|
||||
#include "sm_crc32.h"
|
||||
#include "GameDataFetcher.h"
|
||||
|
||||
#if defined PLATFORM_LINUX
|
||||
#include <dlfcn.h>
|
||||
@ -680,12 +679,14 @@ void GameConfigManager::OnSourceModAllShutdown()
|
||||
|
||||
bool GameConfigManager::LoadGameConfigFile(const char *file, IGameConfig **_pConfig, char *error, size_t maxlength)
|
||||
{
|
||||
#if 0
|
||||
/* A crash was detected during last load - We block the gamedata loading so it hopefully won't happen again */
|
||||
if (g_blockGameDataLoad)
|
||||
{
|
||||
UTIL_Format(error, maxlength, "GameData loaded blocked due to detected crash");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
CGameConfig *pConfig;
|
||||
|
||||
|
@ -1,911 +0,0 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include "GameDataFetcher.h"
|
||||
#include "bitbuf.h"
|
||||
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#define INVALID_SOCKET -1
|
||||
#define closesocket close
|
||||
#define WSAGetLastError() errno
|
||||
#endif
|
||||
|
||||
#include "sh_vector.h"
|
||||
#include "sh_string.h"
|
||||
#include "sm_version.h"
|
||||
|
||||
#if SOURCE_ENGINE == SE_LEFT4DEAD
|
||||
#include "convar_sm_l4d.h"
|
||||
#elif SOURCE_ENGINE == SE_ORANGEBOX
|
||||
#include "convar_sm_ob.h"
|
||||
#else
|
||||
#include "convar_sm.h"
|
||||
#endif
|
||||
|
||||
#include "sourcemm_api.h"
|
||||
#include "time.h"
|
||||
#include "TimerSys.h"
|
||||
#include "compat_wrappers.h"
|
||||
#include "sm_stringutil.h"
|
||||
#include "md5.h"
|
||||
#include "frame_hooks.h"
|
||||
|
||||
#define QUERY_MAX_LENGTH 1024
|
||||
|
||||
static BuildMD5ableBuffer g_MD5Builder;
|
||||
static FetcherThread g_FetchThread;
|
||||
|
||||
static FILE *logfile = NULL;
|
||||
|
||||
bool g_disableGameDataUpdate = false;
|
||||
|
||||
/**
|
||||
* Note on this. If we issue a reload and changelevel, my srcds.exe will emit
|
||||
* Assertion Failed: !m_bServiceStarted
|
||||
* on quit. This seems like a non-issue, because before we just terminated the
|
||||
* server anyway. If anyone notices and files a bug, we can look into it further.
|
||||
*/
|
||||
bool g_restartAfterUpdate = false;
|
||||
|
||||
static bool was_level_started = false;
|
||||
static int g_serverPort = 6500;
|
||||
static char g_serverAddress[100] = "smupdate.alliedmods.net";
|
||||
|
||||
static void _ForceRestart(void *data)
|
||||
{
|
||||
char cmd[300];
|
||||
g_Logger.LogMessage("Automatically restarting SourceMod after a successful gamedata update.");
|
||||
UTIL_Format(cmd, sizeof(cmd), "meta unload %d\n", g_PLID);
|
||||
engine->ServerCommand(cmd);
|
||||
UTIL_Format(cmd, sizeof(cmd), "changelevel \"%s\"\n", STRING(gpGlobals->mapname));
|
||||
engine->ServerCommand(cmd);
|
||||
UTIL_Format(cmd, sizeof(cmd), "echo SourceMod restarted after gamedata update.\n");
|
||||
engine->ServerCommand(cmd);
|
||||
}
|
||||
|
||||
static void ForceRestart()
|
||||
{
|
||||
FrameAction action;
|
||||
|
||||
action.action = _ForceRestart;
|
||||
action.data = NULL;
|
||||
AddFrameAction(action);
|
||||
}
|
||||
|
||||
void FetcherThread::RunThread(IThreadHandle *pHandle)
|
||||
{
|
||||
char lock_path[PLATFORM_MAX_PATH];
|
||||
g_SourceMod.BuildPath(Path_SM, lock_path, sizeof(lock_path), "data/temp");
|
||||
g_LibSys.CreateFolder(lock_path);
|
||||
|
||||
g_SourceMod.BuildPath(Path_SM, lock_path, sizeof(lock_path), "data/temp/gamedata.lock");
|
||||
|
||||
char log_path[PLATFORM_MAX_PATH];
|
||||
g_SourceMod.BuildPath(Path_SM, log_path, sizeof(log_path), "logs/gamedata");
|
||||
|
||||
g_LibSys.CreateFolder(log_path);
|
||||
|
||||
time_t t;
|
||||
GetAdjustedTime(&t);
|
||||
tm *curtime = localtime(&t);
|
||||
|
||||
g_SourceMod.BuildPath(Path_SM,
|
||||
log_path,
|
||||
sizeof(log_path),
|
||||
"logs/gamedata/L%04d%02d%02d.log",
|
||||
curtime->tm_year + 1900,
|
||||
curtime->tm_mon + 1,
|
||||
curtime->tm_mday);
|
||||
|
||||
logfile = fopen(log_path, "at");
|
||||
|
||||
if (!logfile)
|
||||
{
|
||||
/* :( */
|
||||
return;
|
||||
}
|
||||
|
||||
//Create a blank lock file
|
||||
FILE *fp = fopen(lock_path, "w");
|
||||
if (fp)
|
||||
{
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
char query[QUERY_MAX_LENGTH];
|
||||
|
||||
/* Check for updated gamedata files */
|
||||
int len = BuildGameDataQuery(query, QUERY_MAX_LENGTH);
|
||||
|
||||
if (len == 0)
|
||||
{
|
||||
g_Logger.LogToFileOnly(logfile, "Could not build gamedata query!");
|
||||
fclose(logfile);
|
||||
unlink(lock_path);
|
||||
return;
|
||||
}
|
||||
|
||||
/* We check this late so we have the MD5 sums available. This may change in the future. */
|
||||
if (g_disableGameDataUpdate)
|
||||
{
|
||||
g_Logger.LogToFileOnly(logfile, "Skipping gamedata fetcher (DisableAutoUpdate set)");
|
||||
fclose(logfile);
|
||||
unlink(lock_path);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Create a new socket for this connection */
|
||||
int socketDescriptor = ConnectSocket();
|
||||
|
||||
if (socketDescriptor == INVALID_SOCKET)
|
||||
{
|
||||
fclose(logfile);
|
||||
unlink(lock_path);
|
||||
return;
|
||||
}
|
||||
|
||||
int sent = SendData(socketDescriptor, query, len);
|
||||
|
||||
IF_DEBUG_SPEW
|
||||
g_Logger.LogToFileOnly(logfile, "Sent gamedata query");
|
||||
ENDIF_DEBUG_SPEW
|
||||
|
||||
if (sent == 0)
|
||||
{
|
||||
IF_DEBUG_SPEW
|
||||
g_Logger.LogToFileOnly(logfile, "Failed to send gamedata query data to remote host");
|
||||
ENDIF_DEBUG_SPEW
|
||||
|
||||
closesocket(socketDescriptor);
|
||||
fclose(logfile);
|
||||
unlink(lock_path);
|
||||
return;
|
||||
}
|
||||
|
||||
ProcessGameDataQuery(socketDescriptor);
|
||||
|
||||
/* And we're done! */
|
||||
closesocket(socketDescriptor);
|
||||
fclose(logfile);
|
||||
unlink(lock_path);
|
||||
}
|
||||
|
||||
void FetcherThread::OnTerminate(IThreadHandle *pHandle, bool cancel)
|
||||
{
|
||||
g_blockGameDataLoad = false;
|
||||
|
||||
if (cancel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (wasSuccess)
|
||||
{
|
||||
HandleUpdateStatus(updateStatus, build);
|
||||
|
||||
if (needsRestart)
|
||||
{
|
||||
if (g_restartAfterUpdate)
|
||||
{
|
||||
if (was_level_started)
|
||||
{
|
||||
ForceRestart();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_Logger.LogMessage("Your gamedata files have been updated, please restart your server.");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!g_disableGameDataUpdate)
|
||||
{
|
||||
g_Logger.LogError("An error occurred in the gamedata fetcher, see your gamedata log files for more information.");
|
||||
}
|
||||
}
|
||||
|
||||
int FetcherThread::BuildGameDataQuery(char *buffer, int maxlen)
|
||||
{
|
||||
char gamedata_path[PLATFORM_MAX_PATH];
|
||||
g_SourceMod.BuildPath(Path_SM, gamedata_path, sizeof(gamedata_path), "gamedata");
|
||||
|
||||
IDirectory *dir = g_LibSys.OpenDirectory(gamedata_path);
|
||||
|
||||
if (dir == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bf_write Writer = bf_write("GameDataQuery", buffer, maxlen);
|
||||
|
||||
Writer.WriteByte('A'); //Generic Header char
|
||||
Writer.WriteByte('G'); //G for gamedata query, or green, like my hat.
|
||||
|
||||
short build[4] = { SVN_FILE_VERSION };
|
||||
|
||||
Writer.WriteBytes(&build[0], 8);
|
||||
|
||||
Writer.WriteByte(0); // Initialize the file counter - Index 10
|
||||
|
||||
while (dir->MoreFiles())
|
||||
{
|
||||
if (dir->IsEntryFile())
|
||||
{
|
||||
const char *name = dir->GetEntryName();
|
||||
size_t len = strlen(name);
|
||||
if (len >= 4 && strcmp(&name[len-4], ".txt") == 0)
|
||||
{
|
||||
MD5 md5;
|
||||
SMCError err;
|
||||
SMCStates states;
|
||||
unsigned char raw[16];
|
||||
char file[PLATFORM_MAX_PATH];
|
||||
|
||||
g_LibSys.PathFormat(file, sizeof(file), "%s/%s", gamedata_path, name);
|
||||
|
||||
g_MD5Builder.checksum = &md5;
|
||||
if ((err = g_TextParser.ParseFile_SMC(file, &g_MD5Builder, &states)) == SMCError_Okay)
|
||||
{
|
||||
md5.raw_digest(raw);
|
||||
(uint8_t)buffer[10]++; //Increment the file counter
|
||||
Writer.WriteBytes(raw, 16);
|
||||
|
||||
FileData *data = new FileData();
|
||||
data->filename = new SourceHook::String(file);
|
||||
md5.hex_digest(data->checksum);
|
||||
filenames.push_back(data);
|
||||
|
||||
IF_DEBUG_SPEW
|
||||
g_Logger.LogToFileOnly(logfile, "Parsed file: %s as %s", file, data->checksum);
|
||||
ENDIF_DEBUG_SPEW
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
IF_DEBUG_SPEW
|
||||
const char *error = g_TextParser.GetSMCErrorString(err);
|
||||
g_Logger.LogToFileOnly(logfile, "Parsing of file %s failed: %s", file, error);
|
||||
ENDIF_DEBUG_SPEW
|
||||
}
|
||||
}
|
||||
}
|
||||
dir->NextEntry();
|
||||
}
|
||||
|
||||
return Writer.GetNumBytesWritten();
|
||||
}
|
||||
|
||||
int FetcherThread::ConnectSocket()
|
||||
{
|
||||
#if defined PLATFORM_WINDOWS
|
||||
WSADATA wsaData;
|
||||
WSAStartup(0x0101, &wsaData);
|
||||
#endif
|
||||
|
||||
struct protoent *ptrp;
|
||||
|
||||
if ((ptrp = getprotobyname("tcp")) == NULL)
|
||||
{
|
||||
g_Logger.LogToFileOnly(logfile, "Error: Failed to find TCP protocol");
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
int socketDescriptor = socket(AF_INET, SOCK_STREAM, ptrp->p_proto);
|
||||
|
||||
if (socketDescriptor == INVALID_SOCKET)
|
||||
{
|
||||
char error[255];
|
||||
g_LibSys.GetPlatformErrorEx(WSAGetLastError(), error, sizeof(error));
|
||||
g_Logger.LogToFileOnly(logfile, "Error: Failed to create socket: %s", error);
|
||||
closesocket(socketDescriptor);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
struct hostent *he;
|
||||
struct sockaddr_in local_addr;
|
||||
|
||||
local_addr.sin_family = AF_INET;
|
||||
local_addr.sin_port = htons((u_short)g_serverPort);
|
||||
|
||||
he = gethostbyname(g_serverAddress);
|
||||
|
||||
if (!he)
|
||||
{
|
||||
if ((local_addr.sin_addr.s_addr = inet_addr(g_serverAddress)) == INADDR_NONE)
|
||||
{
|
||||
g_Logger.LogToFileOnly(logfile, "Couldn't locate address: %s", g_serverAddress);
|
||||
closesocket(socketDescriptor);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(&local_addr.sin_addr, (struct in_addr *)he->h_addr, he->h_length);
|
||||
}
|
||||
|
||||
if (connect(socketDescriptor, (struct sockaddr *) &local_addr, sizeof(local_addr)) < 0)
|
||||
{
|
||||
char error[255];
|
||||
g_LibSys.GetPlatformErrorEx(WSAGetLastError(), error, sizeof(error));
|
||||
g_Logger.LogToFileOnly(logfile, "Couldn't connect to %s: %s", g_serverAddress, error);
|
||||
closesocket(socketDescriptor);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
return socketDescriptor;
|
||||
}
|
||||
|
||||
void FetcherThread::ProcessGameDataQuery(int socketDescriptor)
|
||||
{
|
||||
char buffer[50];
|
||||
|
||||
IF_DEBUG_SPEW
|
||||
g_Logger.LogToFileOnly(logfile, "Waiting for reply!");
|
||||
ENDIF_DEBUG_SPEW
|
||||
|
||||
//Read in the header bytes
|
||||
int returnLen = RecvData(socketDescriptor, buffer, 12);
|
||||
|
||||
|
||||
if (returnLen == 0)
|
||||
{
|
||||
char error[255];
|
||||
g_LibSys.GetPlatformErrorEx(WSAGetLastError(), error, sizeof(error));
|
||||
g_Logger.LogToFileOnly(logfile, "Did not receive reply: %s", error);
|
||||
return;
|
||||
}
|
||||
|
||||
IF_DEBUG_SPEW
|
||||
g_Logger.LogToFileOnly(logfile, "Received Header!");
|
||||
ENDIF_DEBUG_SPEW
|
||||
|
||||
bf_read Reader = bf_read("GameDataQuery", buffer, 12);
|
||||
|
||||
if (Reader.ReadByte() != 'A' || Reader.ReadByte() != 'G')
|
||||
{
|
||||
IF_DEBUG_SPEW
|
||||
g_Logger.LogToFileOnly(logfile, "Unknown Query Response");
|
||||
ENDIF_DEBUG_SPEW
|
||||
return;
|
||||
}
|
||||
|
||||
updateStatus = (UpdateStatus)Reader.ReadByte();
|
||||
|
||||
build[0] = Reader.ReadShort();
|
||||
build[1] = Reader.ReadShort();
|
||||
build[2] = Reader.ReadShort();
|
||||
build[3] = Reader.ReadShort();
|
||||
|
||||
IF_DEBUG_SPEW
|
||||
g_Logger.LogToFileOnly(logfile,
|
||||
"Update Status: %i - Latest %i.%i.%i.%i",
|
||||
updateStatus,
|
||||
build[0],
|
||||
build[1],
|
||||
build[2],
|
||||
build[3]);
|
||||
ENDIF_DEBUG_SPEW
|
||||
|
||||
int changedFiles = Reader.ReadByte();
|
||||
|
||||
IF_DEBUG_SPEW
|
||||
g_Logger.LogToFileOnly(logfile, "Files to download: %i", changedFiles);
|
||||
ENDIF_DEBUG_SPEW
|
||||
|
||||
for (int i=0; i<changedFiles; i++)
|
||||
{
|
||||
//Read in the file index and byte count
|
||||
returnLen = RecvData(socketDescriptor, buffer, 5);
|
||||
|
||||
if (returnLen == 0)
|
||||
{
|
||||
/* Timeout or fail? */
|
||||
return;
|
||||
}
|
||||
|
||||
Reader.StartReading(buffer, 5);
|
||||
|
||||
int index = Reader.ReadByte();
|
||||
int tempLen = Reader.ReadUBitLong(32);
|
||||
|
||||
IF_DEBUG_SPEW
|
||||
g_Logger.LogToFileOnly(logfile, "File index %i and length %i", index, tempLen);
|
||||
ENDIF_DEBUG_SPEW
|
||||
|
||||
void *memPtr;
|
||||
memtable->CreateMem(tempLen+1, &memPtr);
|
||||
|
||||
//Read the contents of our file into the memtable
|
||||
returnLen = RecvData(socketDescriptor, (char *)memPtr, tempLen);
|
||||
|
||||
IF_DEBUG_SPEW
|
||||
g_Logger.LogToFileOnly(logfile, "Received %i bytes", returnLen);
|
||||
ENDIF_DEBUG_SPEW
|
||||
|
||||
if (returnLen == 0)
|
||||
{
|
||||
/* Timeout or fail? */
|
||||
return;
|
||||
}
|
||||
|
||||
((unsigned char *)memPtr)[tempLen] = '\0';
|
||||
|
||||
FileData *data = filenames.at(index);
|
||||
const char *filename;
|
||||
if (data != NULL)
|
||||
{
|
||||
filename = data->filename->c_str();
|
||||
|
||||
FILE *fp = fopen(filename, "w");
|
||||
|
||||
if (fp)
|
||||
{
|
||||
fprintf(fp, "%s", (const char *)memPtr);
|
||||
fclose(fp);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_Logger.LogToFileOnly(logfile, "Failed to open file \"%s\" for writing", filename);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
filename = "";
|
||||
}
|
||||
|
||||
memtable->Reset();
|
||||
|
||||
g_Logger.LogToFileOnly(logfile, "Updated file: %s", filename);
|
||||
}
|
||||
|
||||
IF_DEBUG_SPEW
|
||||
g_Logger.LogToFileOnly(logfile, "File Downloads Completed!");
|
||||
ENDIF_DEBUG_SPEW
|
||||
|
||||
needsRestart = false;
|
||||
|
||||
if (changedFiles > 0)
|
||||
{
|
||||
needsRestart = true;
|
||||
}
|
||||
|
||||
//Read changed file count
|
||||
returnLen = RecvData(socketDescriptor, buffer, 1);
|
||||
|
||||
if (returnLen == 0)
|
||||
{
|
||||
char error[255];
|
||||
g_LibSys.GetPlatformErrorEx(WSAGetLastError(), error, sizeof(error));
|
||||
g_Logger.LogToFileOnly(logfile, "Did not receive count reply: %s", error);
|
||||
return;
|
||||
}
|
||||
|
||||
Reader.StartReading(buffer, 1);
|
||||
|
||||
changedFiles = Reader.ReadByte();
|
||||
|
||||
if (changedFiles < 1)
|
||||
{
|
||||
IF_DEBUG_SPEW
|
||||
g_Logger.LogToFileOnly(logfile, "No unknown files. We're all done");
|
||||
ENDIF_DEBUG_SPEW
|
||||
return;
|
||||
}
|
||||
|
||||
char *changedFileIndexes = new char[changedFiles];
|
||||
|
||||
IF_DEBUG_SPEW
|
||||
g_Logger.LogToFileOnly(logfile, "%i files were unknown", changedFiles);
|
||||
ENDIF_DEBUG_SPEW
|
||||
|
||||
returnLen = RecvData(socketDescriptor, changedFileIndexes, changedFiles);
|
||||
|
||||
if (returnLen == 0)
|
||||
{
|
||||
char error[255];
|
||||
g_LibSys.GetPlatformErrorEx(WSAGetLastError(), error, sizeof(error));
|
||||
g_Logger.LogToFileOnly(logfile, "Did not receive list reply: %s", error);
|
||||
return;
|
||||
}
|
||||
|
||||
Reader.StartReading(changedFileIndexes, changedFiles);
|
||||
|
||||
for (int i=0; i<changedFiles; i++)
|
||||
{
|
||||
int index = Reader.ReadByte();
|
||||
char fileName[30];
|
||||
|
||||
FileData *data = filenames.at(index);
|
||||
const char* pathname;
|
||||
if (data != NULL)
|
||||
{
|
||||
pathname = data->filename->c_str();
|
||||
}
|
||||
else
|
||||
{
|
||||
pathname = "";
|
||||
}
|
||||
|
||||
g_LibSys.GetFileFromPath(fileName, sizeof(fileName), pathname);
|
||||
IF_DEBUG_SPEW
|
||||
g_Logger.LogToFileOnly(logfile, "Unknown File %i : %s", index, fileName);
|
||||
ENDIF_DEBUG_SPEW
|
||||
}
|
||||
|
||||
delete [] changedFileIndexes;
|
||||
|
||||
wasSuccess = true;
|
||||
}
|
||||
|
||||
int FetcherThread::RecvData(int socketDescriptor, char *buffer, int len)
|
||||
{
|
||||
fd_set fds;
|
||||
struct timeval tv;
|
||||
|
||||
/* Create a 10 Second Timeout */
|
||||
tv.tv_sec = 10;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
int bytesReceivedTotal = 0;
|
||||
|
||||
while (bytesReceivedTotal < len)
|
||||
{
|
||||
/* Add our socket to a socket set */
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(socketDescriptor, &fds);
|
||||
|
||||
/* Wait max of 10 seconds for recv to become available */
|
||||
select(socketDescriptor+1, &fds, NULL, NULL, &tv);
|
||||
|
||||
int bytesReceived = 0;
|
||||
|
||||
/* Is there a limit on how much we can receive? Some site said 1024 bytes, which will be well short of a file */
|
||||
if (FD_ISSET(socketDescriptor, &fds))
|
||||
{
|
||||
bytesReceived = recv(socketDescriptor, buffer+bytesReceivedTotal, len-bytesReceivedTotal, 0);
|
||||
}
|
||||
|
||||
if (bytesReceived == 0 || bytesReceived == -1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bytesReceivedTotal += bytesReceived;
|
||||
}
|
||||
|
||||
return bytesReceivedTotal;
|
||||
}
|
||||
|
||||
int FetcherThread::SendData(int socketDescriptor, char *buffer, int len)
|
||||
{
|
||||
fd_set fds;
|
||||
struct timeval tv;
|
||||
|
||||
tv.tv_sec = 10;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
int sentBytesTotal = 0;
|
||||
|
||||
while (sentBytesTotal < len)
|
||||
{
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(socketDescriptor, &fds);
|
||||
|
||||
select(socketDescriptor+1, NULL, &fds, NULL, &tv);
|
||||
|
||||
int sentBytes = 0;
|
||||
|
||||
if (FD_ISSET(socketDescriptor, &fds))
|
||||
{
|
||||
sentBytes = send(socketDescriptor, buffer+sentBytesTotal, len-sentBytesTotal, 0);
|
||||
}
|
||||
|
||||
if (sentBytes == 0 || sentBytes == -1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
sentBytesTotal += sentBytes;
|
||||
}
|
||||
|
||||
return sentBytesTotal;
|
||||
}
|
||||
|
||||
void FetcherThread::HandleUpdateStatus(UpdateStatus status, short version[4])
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case Update_Unknown:
|
||||
case Update_Current:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
case Update_NewBuild:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
case Update_MinorAvailable:
|
||||
{
|
||||
g_Logger.LogMessage("SourceMod Update: A new release of SourceMod is now available from sourcemod.net");
|
||||
g_Logger.LogMessage("Current Version: %i.%i.%i Available: %i.%i.%i", version[0], version[1], version[2], version[0], version[1], version[2]);
|
||||
break;
|
||||
}
|
||||
|
||||
case Update_MajorAvailable:
|
||||
{
|
||||
g_Logger.LogMessage("SourceMod Update: An major release of SourceMod is now available from sourcemod.net");
|
||||
g_Logger.LogMessage("Current Version: %i.%i.%i Available: %i.%i.%i", version[0], version[1], version[2], version[0], version[1], version[2]);
|
||||
break;
|
||||
}
|
||||
|
||||
case Update_CriticalAvailable:
|
||||
{
|
||||
g_Logger.LogError("SourceMod Update: A critical SourceMod release is available from sourcemod.net. It is strongly recommended that you update!");
|
||||
g_Logger.LogMessage("Current Version: %i.%i.%i Available: %i.%i.%i", version[0], version[1], version[2], version[0], version[1], version[2]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool g_blockGameDataLoad = false;
|
||||
static IThreadHandle *fetch_thread_hndl;
|
||||
|
||||
class InitFetch : public SMGlobalClass
|
||||
{
|
||||
public:
|
||||
void OnSourceModAllInitialized_Post()
|
||||
{
|
||||
char lock_path[PLATFORM_MAX_PATH];
|
||||
g_SourceMod.BuildPath(Path_SM, lock_path, sizeof(lock_path), "data/temp/gamedata.lock");
|
||||
|
||||
if (g_LibSys.IsPathFile(lock_path) && g_LibSys.PathExists(lock_path))
|
||||
{
|
||||
g_Logger.LogError("sourcemod/data/temp/gamedata.lock file detected. This is most likely due to a crash during GameData updating - Blocking GameData loading");
|
||||
g_Logger.LogError("If this error persists delete the file manually");
|
||||
g_blockGameDataLoad = true;
|
||||
}
|
||||
|
||||
ThreadParams fetchThreadParams = ThreadParams();
|
||||
fetchThreadParams.prio = ThreadPrio_Low;
|
||||
fetch_thread_hndl = g_pThreader->MakeThread(&g_FetchThread, &fetchThreadParams);
|
||||
}
|
||||
|
||||
void OnSourceModShutdown()
|
||||
{
|
||||
fetch_thread_hndl->WaitForThread();
|
||||
fetch_thread_hndl->DestroyThis();
|
||||
}
|
||||
|
||||
void OnSourceModLevelActivated()
|
||||
{
|
||||
was_level_started = true;
|
||||
|
||||
if (g_restartAfterUpdate &&
|
||||
g_FetchThread.wasSuccess &&
|
||||
g_FetchThread.needsRestart)
|
||||
{
|
||||
ForceRestart();
|
||||
}
|
||||
}
|
||||
|
||||
void OnSourceModLevelEnd()
|
||||
{
|
||||
was_level_started = false;
|
||||
}
|
||||
|
||||
ConfigResult OnSourceModConfigChanged(const char *key,
|
||||
const char *value,
|
||||
ConfigSource source,
|
||||
char *error,
|
||||
size_t maxlength)
|
||||
{
|
||||
if (strcasecmp(key, "DisableAutoUpdate") == 0)
|
||||
{
|
||||
if (strcasecmp(value, "yes") == 0)
|
||||
{
|
||||
g_disableGameDataUpdate = true;
|
||||
return ConfigResult_Accept;
|
||||
}
|
||||
else if (strcasecmp(value, "no") == 0)
|
||||
{
|
||||
g_disableGameDataUpdate = false;
|
||||
return ConfigResult_Accept;
|
||||
}
|
||||
|
||||
return ConfigResult_Reject;
|
||||
}
|
||||
|
||||
if (strcasecmp(key, "ForceRestartAfterUpdate") == 0)
|
||||
{
|
||||
if (strcasecmp(value, "yes") == 0)
|
||||
{
|
||||
g_restartAfterUpdate = true;
|
||||
return ConfigResult_Accept;
|
||||
}
|
||||
else if (strcasecmp(value, "no") == 0)
|
||||
{
|
||||
g_restartAfterUpdate = false;
|
||||
return ConfigResult_Accept;
|
||||
}
|
||||
|
||||
return ConfigResult_Reject;
|
||||
}
|
||||
|
||||
if (strcasecmp(key, "AutoUpdateServer") == 0)
|
||||
{
|
||||
UTIL_Format(g_serverAddress, sizeof(g_serverAddress), "%s", value);
|
||||
|
||||
return ConfigResult_Accept;
|
||||
}
|
||||
|
||||
if (strcasecmp(key, "AutoUpdatePort") == 0)
|
||||
{
|
||||
int port = atoi(value);
|
||||
|
||||
if (!port)
|
||||
{
|
||||
return ConfigResult_Reject;
|
||||
}
|
||||
|
||||
g_serverPort = port;
|
||||
|
||||
return ConfigResult_Accept;
|
||||
}
|
||||
|
||||
return ConfigResult_Ignore;
|
||||
}
|
||||
} g_InitFetch;
|
||||
|
||||
BuildMD5ableBuffer::BuildMD5ableBuffer()
|
||||
{
|
||||
stringTable = new BaseStringTable(2048);
|
||||
}
|
||||
|
||||
BuildMD5ableBuffer::~BuildMD5ableBuffer()
|
||||
{
|
||||
delete stringTable;
|
||||
}
|
||||
|
||||
void BuildMD5ableBuffer::ReadSMC_ParseStart()
|
||||
{
|
||||
stringTable->Reset();
|
||||
}
|
||||
|
||||
SMCResult BuildMD5ableBuffer::ReadSMC_KeyValue(const SMCStates *states,
|
||||
const char *key,
|
||||
const char *value)
|
||||
{
|
||||
stringTable->AddString(key);
|
||||
stringTable->AddString(value);
|
||||
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
SMCResult BuildMD5ableBuffer::ReadSMC_NewSection(const SMCStates *states, const char *name)
|
||||
{
|
||||
stringTable->AddString(name);
|
||||
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
void BuildMD5ableBuffer::ReadSMC_ParseEnd(bool halted, bool failed)
|
||||
{
|
||||
if (halted || failed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void *data = stringTable->GetMemTable()->GetAddress(0);
|
||||
|
||||
if (data != NULL)
|
||||
{
|
||||
checksum->update((unsigned char *)data, stringTable->GetMemTable()->GetActualMemUsed());
|
||||
}
|
||||
|
||||
checksum->finalize();
|
||||
}
|
||||
|
||||
CON_COMMAND(sm_gamedata_md5, "Checks the MD5 sum for a given gamedata file")
|
||||
{
|
||||
#if SOURCE_ENGINE == SE_EPISODEONE
|
||||
CCommand args;
|
||||
#endif
|
||||
|
||||
if (args.ArgC() < 2)
|
||||
{
|
||||
g_SMAPI->ConPrint("Usage: sm_gamedata_md5 <file>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
const char *file = args.Arg(1);
|
||||
if (!file || file[0] == '\0')
|
||||
{
|
||||
g_SMAPI->ConPrint("Usage: sm_gamedata_md5 <file>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
SourceHook::CVector<FileData *>::iterator iter = g_FetchThread.filenames.begin();
|
||||
|
||||
FileData *curData;
|
||||
|
||||
while (iter != g_FetchThread.filenames.end())
|
||||
{
|
||||
curData = (*iter);
|
||||
|
||||
char fileName[30];
|
||||
|
||||
g_LibSys.GetFileFromPath(fileName, sizeof(fileName), curData->filename->c_str());
|
||||
|
||||
if (strcmpi(fileName, file) == 0)
|
||||
{
|
||||
g_SMAPI->ConPrintf("MD5 Sum: %s\n", curData->checksum);
|
||||
return;
|
||||
}
|
||||
|
||||
iter++;
|
||||
}
|
||||
|
||||
g_SMAPI->ConPrint("File not found!\n");
|
||||
}
|
||||
|
||||
FetcherThread::~FetcherThread()
|
||||
{
|
||||
SourceHook::CVector<FileData *>::iterator iter = filenames.begin();
|
||||
|
||||
FileData *curData;
|
||||
|
||||
while (iter != filenames.end())
|
||||
{
|
||||
curData = (*iter);
|
||||
delete curData->filename;
|
||||
delete curData;
|
||||
iter = filenames.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
FetcherThread::FetcherThread()
|
||||
{
|
||||
memtable = new BaseMemTable(4096);
|
||||
wasSuccess = false;
|
||||
needsRestart = false;
|
||||
}
|
||||
|
@ -1,106 +0,0 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_GAMEDATAFETCHER_H_
|
||||
#define _INCLUDE_SOURCEMOD_GAMEDATAFETCHER_H_
|
||||
|
||||
#include "sourcemod.h"
|
||||
#include "TextParsers.h"
|
||||
#include "IThreader.h"
|
||||
#include "Logger.h"
|
||||
#include "LibrarySys.h"
|
||||
#include "ThreadSupport.h"
|
||||
#include "sm_memtable.h"
|
||||
#include <sh_string.h>
|
||||
#include <sh_list.h>
|
||||
|
||||
enum UpdateStatus
|
||||
{
|
||||
Update_Unknown = 0, /* Version wasn't recognised or version querying is unsupported */
|
||||
Update_Current = 1, /* Server is running latest version */
|
||||
Update_NewBuild = 2, /* Server is on a svn release and a newer version is available */
|
||||
Update_MinorAvailable = 3, /* Server is on a release and a minor release has superceeded it */
|
||||
Update_MajorAvailable = 4, /* Server is on a release and a major release has superceeded it */
|
||||
Update_CriticalAvailable = 5, /* A critical update has been released (security fixes etc) */
|
||||
};
|
||||
|
||||
class MD5;
|
||||
|
||||
class BuildMD5ableBuffer : public ITextListener_SMC
|
||||
{
|
||||
public:
|
||||
BuildMD5ableBuffer();
|
||||
~BuildMD5ableBuffer();
|
||||
void ReadSMC_ParseStart();
|
||||
SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value);
|
||||
SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name);
|
||||
void ReadSMC_ParseEnd(bool halted, bool failed);
|
||||
public:
|
||||
MD5 *checksum;
|
||||
private:
|
||||
BaseStringTable *stringTable;
|
||||
};
|
||||
|
||||
struct FileData
|
||||
{
|
||||
SourceHook::String *filename;
|
||||
char checksum[33];
|
||||
};
|
||||
|
||||
class FetcherThread : public IThread
|
||||
{
|
||||
public:
|
||||
FetcherThread();
|
||||
~FetcherThread();
|
||||
public:
|
||||
void RunThread(IThreadHandle *pHandle);
|
||||
void OnTerminate(IThreadHandle *pHandle, bool cancel);
|
||||
private:
|
||||
int BuildGameDataQuery(char *buffer, int maxlen);
|
||||
void ProcessGameDataQuery(int SocketDescriptor);
|
||||
int RecvData(int socketDescriptor, char *buffer, int len);
|
||||
int SendData(int socketDescriptor, char *buffer, int len);
|
||||
int ConnectSocket();
|
||||
void HandleUpdateStatus(UpdateStatus status, short version[4]);
|
||||
public:
|
||||
SourceHook::CVector<FileData *> filenames;
|
||||
bool needsRestart;
|
||||
bool wasSuccess;
|
||||
private:
|
||||
UpdateStatus updateStatus;
|
||||
BaseMemTable *memtable;
|
||||
short build[4];
|
||||
};
|
||||
|
||||
extern bool g_blockGameDataLoad;
|
||||
|
||||
#endif // _INCLUDE_SOURCEMOD_GAMEDATAFETCHER_H_
|
||||
|
@ -19,7 +19,7 @@ OBJECTS = AdminCache.cpp CDataPack.cpp ConCmdManager.cpp ConVarManager.cpp CoreC
|
||||
sourcemm_api.cpp sourcemod.cpp MenuStyle_Base.cpp MenuStyle_Valve.cpp MenuManager.cpp \
|
||||
MenuStyle_Radio.cpp ChatTriggers.cpp ADTFactory.cpp MenuVoting.cpp sm_crc32.cpp \
|
||||
frame_hooks.cpp concmd_cleaner.cpp Profiler.cpp PhraseCollection.cpp NextMap.cpp \
|
||||
NativeOwner.cpp GameDataFetcher.cpp md5.cpp
|
||||
NativeOwner.cpp md5.cpp
|
||||
OBJECTS += smn_admin.cpp smn_bitbuffer.cpp smn_console.cpp smn_core.cpp \
|
||||
smn_datapacks.cpp smn_entities.cpp smn_events.cpp smn_fakenatives.cpp \
|
||||
smn_filesystem.cpp smn_float.cpp smn_functions.cpp smn_gameconfigs.cpp smn_halflife.cpp \
|
||||
|
@ -1141,10 +1141,6 @@
|
||||
RelativePath="..\GameConfigs.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\GameDataFetcher.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\HalfLife2.cpp"
|
||||
>
|
||||
@ -1351,10 +1347,6 @@
|
||||
RelativePath="..\GameConfigs.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\GameDataFetcher.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\HalfLife2.h"
|
||||
>
|
||||
|
@ -1,76 +0,0 @@
|
||||
# (C)2004-2008 SourceMod Development Team
|
||||
# Makefile written by David "BAILOPAN" Anderson
|
||||
|
||||
|
||||
#####################################
|
||||
### EDIT BELOW FOR OTHER PROJECTS ###
|
||||
#####################################
|
||||
|
||||
OBJECTS = smud.cpp smud_connections.cpp smud_threads.cpp
|
||||
|
||||
##############################################
|
||||
### CONFIGURE ANY OTHER FLAGS/OPTIONS HERE ###
|
||||
##############################################
|
||||
|
||||
C_OPT_FLAGS = -DNDEBUG -O3 -funroll-loops -pipe -fno-strict-aliasing
|
||||
C_DEBUG_FLAGS = -D_DEBUG -DDEBUG -g -ggdb3
|
||||
C_GCC4_FLAGS = -fvisibility=hidden
|
||||
CPP_GCC4_FLAGS = -fvisibility-inlines-hidden
|
||||
CPP = gcc
|
||||
|
||||
BINARY = smupdated
|
||||
|
||||
LINK += -lpthread -static-libgcc
|
||||
|
||||
INCLUDE += -I.
|
||||
|
||||
CFLAGS += -D_LINUX -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp -Dstrnicmp=strncasecmp \
|
||||
-D_snprintf=snprintf -D_vsnprintf=vsnprintf -D_alloca=alloca -Dstrcmpi=strcasecmp -Wall -Werror \
|
||||
-Wno-uninitialized -mfpmath=sse -msse -DHAVE_STDINT_H -DSM_DEFAULT_THREADER -m32
|
||||
CPPFLAGS += -Wno-non-virtual-dtor -fno-exceptions -fno-rtti
|
||||
|
||||
################################################
|
||||
### DO NOT EDIT BELOW HERE FOR MOST PROJECTS ###
|
||||
################################################
|
||||
|
||||
ifeq "$(DEBUG)" "true"
|
||||
BIN_DIR = Debug
|
||||
CFLAGS += $(C_DEBUG_FLAGS)
|
||||
else
|
||||
BIN_DIR = Release
|
||||
CFLAGS += $(C_OPT_FLAGS)
|
||||
endif
|
||||
|
||||
GCC_VERSION := $(shell $(CPP) -dumpversion >&1 | cut -b1)
|
||||
ifeq "$(GCC_VERSION)" "4"
|
||||
CFLAGS += $(C_GCC4_FLAGS)
|
||||
CPPFLAGS += $(CPP_GCC4_FLAGS)
|
||||
endif
|
||||
|
||||
OBJ_LINUX := $(OBJECTS:%vm_engine.cpp=$(BIN_DIR)/%vm_engine.o)
|
||||
OBJ_LINUX := $(OBJ_LINUX:%.cpp=$(BIN_DIR)/%.o)
|
||||
OBJ_LINUX := $(OBJ_LINUX:%.c=$(BIN_DIR)/%.o)
|
||||
|
||||
$(BIN_DIR)/%vm_engine.o: %vm_engine.cpp
|
||||
$(CPP) $(CFLAGS) $(CPPFLAGS) -o $@ -c $<
|
||||
|
||||
$(BIN_DIR)/%.o: %.cpp
|
||||
$(CPP) $(INCLUDE) $(CFLAGS) $(CPPFLAGS) -o $@ -c $<
|
||||
|
||||
$(BIN_DIR)/%.o: %.c
|
||||
$(CPP) $(INCLUDE) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
all:
|
||||
mkdir -p $(BIN_DIR)
|
||||
$(MAKE) -f Makefile sourcemod
|
||||
|
||||
sourcemod: $(OBJ_LINUX)
|
||||
$(CPP) $(INCLUDE) $(OBJ_LINUX) $(LINK) -m32 -lstdc++ -o $(BIN_DIR)/$(BINARY)
|
||||
|
||||
debug:
|
||||
$(MAKE) -f Makefile all DEBUG=true
|
||||
|
||||
default: all
|
||||
|
||||
clean:
|
||||
rm -rf $(BIN_DIR)/$(BINARY)
|
@ -1,157 +0,0 @@
|
||||
#include <errno.h>
|
||||
#include "smud.h"
|
||||
#include "smud_threads.h"
|
||||
|
||||
#define LISTEN_PORT 6500
|
||||
#define LISTEN_QUEUE_LENGTH 6
|
||||
|
||||
char fileNames[NUM_FILES][30] = {
|
||||
"core.games.txt",
|
||||
"sdktools.games.txt",
|
||||
"sdktools.games.ep2.txt",
|
||||
"sdktools.games.l4d.txt",
|
||||
"sm-cstrike.games.txt",
|
||||
"sm-tf2.games.txt",
|
||||
};
|
||||
|
||||
void *fileLocations[NUM_FILES];
|
||||
int fileLength[NUM_FILES];
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
ThreadPool *pool;
|
||||
struct protoent *pProtocol;
|
||||
struct sockaddr_in serverAddress;
|
||||
struct sockaddr_in clientAddress;
|
||||
int serverSocket;
|
||||
int clientSocket;
|
||||
int addressLen;
|
||||
int opts;
|
||||
int file;
|
||||
char filename[100];
|
||||
struct stat sbuf;
|
||||
|
||||
#if defined DEBUG
|
||||
fprintf(stdout, "Loading Gamedata files into memory\n");
|
||||
#endif
|
||||
|
||||
for (int i=0; i<NUM_FILES; i++)
|
||||
{
|
||||
snprintf(filename, sizeof(filename), "./md5/%s", fileNames[i]);
|
||||
file = open(filename, O_RDWR);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
fprintf(stderr, "Could not find file: %s", filename);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (stat(filename, &sbuf) == -1)
|
||||
{
|
||||
fprintf(stderr, "Could not stat file: %s (error: %s)", filename, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((fileLocations[i] = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, file, 0)) == MAP_FAILED)
|
||||
{
|
||||
fprintf(stderr, "Could not mmap file: %s (error: %s)", filename, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
fileLength[i] = sbuf.st_size;
|
||||
|
||||
#if defined DEBUG
|
||||
fprintf(stdout, "Initialised file of %s of length %i\n", fileNames[i], fileLength[i]);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined DEBUG
|
||||
fprintf(stdout, "Initializing Thread Pool\n");
|
||||
#endif
|
||||
|
||||
pool = new ThreadPool();
|
||||
|
||||
if (!pool->Start())
|
||||
{
|
||||
fprintf(stderr, "Could not initialize thread pool!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if defined DEBUG
|
||||
printf("Create Server Socket\n");
|
||||
#endif
|
||||
|
||||
memset(&serverAddress, 0, sizeof(serverAddress));
|
||||
serverAddress.sin_family = AF_INET;
|
||||
serverAddress.sin_addr.s_addr = INADDR_ANY;
|
||||
serverAddress.sin_port = htons(LISTEN_PORT);
|
||||
|
||||
pProtocol = getprotobyname("tcp");
|
||||
|
||||
if (pProtocol == NULL)
|
||||
{
|
||||
fprintf(stderr, "Could not get tcp proto: %s", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
serverSocket = socket(AF_INET, SOCK_STREAM, pProtocol->p_proto);
|
||||
|
||||
if (serverSocket < 0)
|
||||
{
|
||||
fprintf(stderr, "Could not open socket: %s", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
opts = 1;
|
||||
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &opts, sizeof(opts));
|
||||
|
||||
if (bind(serverSocket, (struct sockaddr *)&serverAddress, sizeof(serverAddress)) < 0)
|
||||
{
|
||||
fprintf(stderr, "Could not bind socket: %s", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (listen(serverSocket, LISTEN_QUEUE_LENGTH) < 0)
|
||||
{
|
||||
fprintf(stderr, "Could not listen on socket: %s", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
fprintf(stdout, "Server has started.\n");
|
||||
|
||||
while (1)
|
||||
{
|
||||
addressLen = sizeof(clientAddress);
|
||||
|
||||
clientSocket = accept(serverSocket,
|
||||
(struct sockaddr *)&clientAddress,
|
||||
(socklen_t *)&addressLen);
|
||||
if (clientSocket < 0)
|
||||
{
|
||||
fprintf(stderr, "Could not accept client: %s", strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
opts = fcntl(clientSocket, F_GETFL, 0);
|
||||
if (fcntl(clientSocket, F_SETFL, opts|O_NONBLOCK) < 0)
|
||||
{
|
||||
fprintf(stderr, "Could not non-block client: %s", strerror(errno));
|
||||
closesocket(clientSocket);
|
||||
continue;
|
||||
}
|
||||
|
||||
#if defined DEBUG
|
||||
fprintf(stdout,
|
||||
"Accepting connection from client (sock %d, ip %s)",
|
||||
clientSocket,
|
||||
inet_ntoa(clientAddress.sin_addr));
|
||||
#endif
|
||||
|
||||
pool->AddConnection(clientSocket);
|
||||
}
|
||||
|
||||
delete pool;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,25 +0,0 @@
|
||||
#ifndef _INCLUDE_SMUD_MAIN_H_
|
||||
#define _INCLUDE_SMUD_MAIN_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define closesocket close
|
||||
|
||||
#define NUM_FILES 6
|
||||
|
||||
extern char fileNames[NUM_FILES][30];
|
||||
extern void *fileLocations[NUM_FILES];
|
||||
extern int fileLength[NUM_FILES];
|
||||
|
||||
#endif //_INCLUDE_SMUD_MAIN_H_
|
@ -1,541 +0,0 @@
|
||||
#include <assert.h>
|
||||
#include "smud_connections.h"
|
||||
#include "smud.h"
|
||||
|
||||
ConnectionPool::ConnectionPool()
|
||||
{
|
||||
pthread_mutex_init(&m_AddLock, NULL);
|
||||
m_timeOut = 1000;
|
||||
}
|
||||
|
||||
ConnectionPool::~ConnectionPool()
|
||||
{
|
||||
pthread_mutex_destroy(&m_AddLock);
|
||||
}
|
||||
|
||||
void ConnectionPool::AddConnection( int fd )
|
||||
{
|
||||
smud_connection *connection = new smud_connection(fd);
|
||||
|
||||
pthread_mutex_lock(&m_AddLock);
|
||||
m_AddQueue.push_back(connection);
|
||||
pthread_mutex_unlock(&m_AddLock);
|
||||
}
|
||||
|
||||
void ConnectionPool::Process( bool *terminate )
|
||||
{
|
||||
struct timespec ts_wait;
|
||||
|
||||
ts_wait.tv_sec = 0;
|
||||
ts_wait.tv_nsec = 50000000; /* 50ms */
|
||||
|
||||
std::list<smud_connection *>::iterator iter;
|
||||
smud_connection *con = NULL;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (*terminate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
iter = m_Links.begin();
|
||||
|
||||
QueryResult result = QueryResult_Continue;
|
||||
int pollReturn = 0;
|
||||
|
||||
/* Add all connections that want processing to the sets */
|
||||
while (iter != m_Links.end())
|
||||
{
|
||||
con = *iter;
|
||||
|
||||
pollReturn = poll(&(con->pollData), 1, 0);
|
||||
assert(pollReturn <= 1);
|
||||
|
||||
if (pollReturn == -1)
|
||||
{
|
||||
//Something went badly wrong or the connection closed.
|
||||
result = QueryResult_Complete;
|
||||
}
|
||||
else if (pollReturn == 1)
|
||||
{
|
||||
//Poll returns the number of sockets available (which can only ever be 1)
|
||||
result = ProcessConnection(con);
|
||||
}
|
||||
|
||||
if (result == QueryResult_Complete)
|
||||
{
|
||||
iter = m_Links.erase(iter);
|
||||
#if defined DEBUG
|
||||
fprintf(stdout, "Closing socket %d\n", con->fd);
|
||||
#endif
|
||||
closesocket(con->fd);
|
||||
delete con;
|
||||
continue;
|
||||
}
|
||||
|
||||
iter++;
|
||||
}
|
||||
|
||||
/* Add new items to process */
|
||||
|
||||
iter = m_Links.end();
|
||||
pthread_mutex_lock(&m_AddLock);
|
||||
m_Links.splice(iter, m_AddQueue);
|
||||
pthread_mutex_unlock(&m_AddLock);
|
||||
|
||||
nanosleep(&ts_wait, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
QueryResult ConnectionPool::ProcessConnection( smud_connection *con )
|
||||
{
|
||||
switch (con->state)
|
||||
{
|
||||
case ConnectionState_ReadQueryHeader:
|
||||
{
|
||||
ReadQueryHeader(con);
|
||||
break;
|
||||
}
|
||||
|
||||
case ConnectionState_ReadQueryData:
|
||||
{
|
||||
ReadQueryContent(con);
|
||||
break;
|
||||
}
|
||||
|
||||
case ConnectionState_ReplyQuery:
|
||||
{
|
||||
ReplyQuery(con);
|
||||
break;
|
||||
}
|
||||
|
||||
case ConnectionState_SendingFiles:
|
||||
{
|
||||
SendFile(con);
|
||||
break;
|
||||
}
|
||||
|
||||
case ConnectionState_SendUnknownList:
|
||||
{
|
||||
SendUnknownList(con);
|
||||
break;
|
||||
}
|
||||
|
||||
case ConnectionState_Complete:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (con->state == ConnectionState_Complete)
|
||||
{
|
||||
printf("Ending connection because it marked itself as finished\n");
|
||||
return QueryResult_Complete;
|
||||
}
|
||||
|
||||
if (con->start + m_timeOut < time(NULL))
|
||||
{
|
||||
printf("Ending connection because it has passed maximum allowed time\n");
|
||||
return QueryResult_Complete;
|
||||
}
|
||||
|
||||
return QueryResult_Continue;
|
||||
}
|
||||
|
||||
void ConnectionPool::ReadQueryHeader( smud_connection *con )
|
||||
{
|
||||
if (con->buffer == NULL)
|
||||
{
|
||||
con->buffer = new char[QUERY_HEADER_SIZE];
|
||||
con->writtenCount = 0;
|
||||
}
|
||||
|
||||
int bytesReceived = 0;
|
||||
|
||||
bytesReceived = recv(con->fd, con->buffer+con->writtenCount, QUERY_HEADER_SIZE-con->writtenCount, 0);
|
||||
|
||||
if (bytesReceived == -1)
|
||||
{
|
||||
if (errno != EAGAIN && errno != EWOULDBLOCK)
|
||||
{
|
||||
con->state = ConnectionState_Complete;
|
||||
delete [] con->buffer;
|
||||
con->buffer = NULL;
|
||||
con->writtenCount = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
con->writtenCount += bytesReceived;
|
||||
|
||||
assert(con->writtenCount <= QUERY_HEADER_SIZE);
|
||||
|
||||
if (con->writtenCount < QUERY_HEADER_SIZE)
|
||||
{
|
||||
/* Don't change the connection status, so next cycle we will come back to here and continue receiving data */
|
||||
return;
|
||||
}
|
||||
|
||||
if (con->buffer[0] != 'A' || con->buffer[1] != 'G')
|
||||
{
|
||||
con->state = ConnectionState_Complete;
|
||||
delete [] con->buffer;
|
||||
con->buffer = NULL;
|
||||
con->writtenCount = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
//Ignore the next 8 bytes for the moment. Versioning data is currently unused
|
||||
// uint16[4] - source version major/minor/something/rev
|
||||
|
||||
con->sentSums = con->buffer[10];
|
||||
|
||||
con->state = ConnectionState_ReadQueryData;
|
||||
#if defined DEBUG
|
||||
fprintf(stdout, "Query Header Read Complete, %i md5's expected\n", con->sentSums);
|
||||
#endif
|
||||
|
||||
delete [] con->buffer;
|
||||
con->buffer = NULL;
|
||||
con->writtenCount = 0;
|
||||
}
|
||||
|
||||
void ConnectionPool::ReplyQuery(smud_connection *con)
|
||||
{
|
||||
char data[12];
|
||||
data[0] = 'A';
|
||||
data[1] = 'G';
|
||||
|
||||
data[2] = (char)Update_Unknown; //unused versioning crap
|
||||
*(short *)&data[3] = 1;
|
||||
*(short *)&data[5] = 0;
|
||||
*(short *)&data[7] = 0;
|
||||
*(short *)&data[9] = 3;
|
||||
|
||||
data[11] = (char)con->sendCount;
|
||||
|
||||
int bytesSent = send(con->fd, data+con->writtenCount, sizeof(data)-con->writtenCount, 0);
|
||||
|
||||
if (bytesSent == -1)
|
||||
{
|
||||
if (errno != EAGAIN && errno != EWOULDBLOCK)
|
||||
{
|
||||
con->state = ConnectionState_Complete;
|
||||
con->writtenCount = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
con->writtenCount += bytesSent;
|
||||
|
||||
assert(con->writtenCount <= 12);
|
||||
|
||||
if (con->writtenCount < 12)
|
||||
{
|
||||
/** Still more data needs to be sent - Return so we come back here next cycle */
|
||||
return;
|
||||
}
|
||||
|
||||
con->state = ConnectionState_SendingFiles;
|
||||
con->writtenCount = 0;
|
||||
#if defined DEBUG
|
||||
printf("Query Reply Header Complete\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void ConnectionPool::ReadQueryContent( smud_connection *con )
|
||||
{
|
||||
if (con->buffer == NULL)
|
||||
{
|
||||
con->buffer = new char[QUERY_CONTENT_SIZE*con->sentSums];
|
||||
con->writtenCount = 0;
|
||||
}
|
||||
|
||||
int bytesReceived = 0;
|
||||
|
||||
bytesReceived = recv(con->fd, con->buffer+con->writtenCount, (QUERY_CONTENT_SIZE*con->sentSums)-con->writtenCount, 0);
|
||||
|
||||
if (bytesReceived == -1)
|
||||
{
|
||||
if (errno != EAGAIN && errno != EWOULDBLOCK)
|
||||
{
|
||||
con->state = ConnectionState_Complete;
|
||||
delete [] con->buffer;
|
||||
con->buffer = NULL;
|
||||
con->writtenCount = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
con->writtenCount += bytesReceived;
|
||||
|
||||
assert(con->writtenCount <= (QUERY_CONTENT_SIZE*con->sentSums));
|
||||
|
||||
if (con->writtenCount < (QUERY_CONTENT_SIZE*con->sentSums))
|
||||
{
|
||||
/* Don't change the connection status, so next cycle we will come back to here and continue receiving data */
|
||||
return;
|
||||
}
|
||||
|
||||
con->shouldSend = new MD5Status[con->sentSums]();
|
||||
con->fileLocation = new int[con->sentSums]();
|
||||
con->headerSent = new bool[con->sentSums]();
|
||||
|
||||
for (int i=0; i<con->sentSums; i++)
|
||||
{
|
||||
con->fileLocation[i] = -1;
|
||||
con->shouldSend[i] = GetMD5UpdateStatus(con->buffer + (QUERY_CONTENT_SIZE*i), con, i);
|
||||
|
||||
if (con->shouldSend[i] == MD5Status_NeedsUpdate)
|
||||
{
|
||||
#if defined DEBUG
|
||||
fprintf(stdout, "File %i needs updating\n", i);
|
||||
#endif
|
||||
con->sendCount++;
|
||||
con->headerSent[i] = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (con->shouldSend[i] == MD5Status_Unknown)
|
||||
{
|
||||
#if defined DEBUG
|
||||
fprintf(stdout, "File %i is unknown\n", i);
|
||||
#endif
|
||||
con->unknownCount++;
|
||||
}
|
||||
}
|
||||
|
||||
con->state = ConnectionState_ReplyQuery;
|
||||
con->pollData.events = POLLOUT;
|
||||
delete [] con->buffer;
|
||||
con->buffer = NULL;
|
||||
con->writtenCount = 0;
|
||||
#if defined DEBUG
|
||||
fprintf(stdout, "Query Data Read Complete\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
MD5Status ConnectionPool::GetMD5UpdateStatus( const char *md5 , smud_connection *con, int fileNum)
|
||||
{
|
||||
//Try find a file with this name in some directory.
|
||||
char path[100] = "./md5/";
|
||||
char temp[4];
|
||||
char md5String[33] = "";
|
||||
|
||||
for (int i=0; i<16; i++)
|
||||
{
|
||||
snprintf(temp, sizeof(temp), "%02x", (unsigned char)md5[i]);
|
||||
strcat(md5String, temp);
|
||||
}
|
||||
|
||||
strcat(path, md5String);
|
||||
|
||||
#if defined DEBUG
|
||||
fprintf(stdout, "checking for file \"%s\"\n", path);
|
||||
#endif
|
||||
|
||||
FILE *file = fopen(path, "r");
|
||||
|
||||
if (file == NULL)
|
||||
{
|
||||
printf("Couldn't find file!\n");
|
||||
return MD5Status_Unknown;
|
||||
}
|
||||
|
||||
char latestMD5[33];
|
||||
fgets(latestMD5, 33, file);
|
||||
#if defined DEBUG
|
||||
fprintf(stdout, "Latest md5 is: %s\n", latestMD5);
|
||||
#endif
|
||||
|
||||
if (strcmp(latestMD5, md5String) == 0)
|
||||
{
|
||||
fclose(file);
|
||||
return MD5Status_Current;
|
||||
}
|
||||
|
||||
char filename[100];
|
||||
filename[0] = '\n';
|
||||
|
||||
while (filename[0] == '\n')
|
||||
{
|
||||
fgets(filename, sizeof(filename), file);
|
||||
}
|
||||
|
||||
if (filename[strlen(filename)-1] == '\n')
|
||||
{
|
||||
filename[strlen(filename)-1] = '\0';
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
#if defined DEBUG
|
||||
fprintf(stdout, "Filename is %s\n", filename);
|
||||
#endif
|
||||
|
||||
//We now need to match this filename with one of our mmap'd files in memory and store it until send gets called.
|
||||
for (int i=0; i<NUM_FILES; i++)
|
||||
{
|
||||
if (strcmp(fileNames[i], filename) == 0)
|
||||
{
|
||||
con->fileLocation[fileNum] = i;
|
||||
#if defined DEBUG
|
||||
fprintf(stdout, "File %i mapped to local file %i\n", fileNum, i);
|
||||
#endif
|
||||
return MD5Status_NeedsUpdate;
|
||||
}
|
||||
}
|
||||
|
||||
return MD5Status_Unknown;
|
||||
}
|
||||
|
||||
void ConnectionPool::SendFile( smud_connection *con )
|
||||
{
|
||||
//Find the next file to send.
|
||||
while (con->writtenCount == 0 &&
|
||||
con->currentFile < con->sentSums &&
|
||||
con->shouldSend[con->currentFile] != MD5Status_NeedsUpdate)
|
||||
{
|
||||
con->currentFile++;
|
||||
}
|
||||
|
||||
//All files have been sent.
|
||||
if (con->currentFile >= con->sentSums)
|
||||
{
|
||||
#if defined DEBUG
|
||||
fprintf(stdout, "All files sent!\n");
|
||||
#endif
|
||||
con->state = ConnectionState_SendUnknownList;
|
||||
con->writtenCount = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
void *file = fileLocations[con->fileLocation[con->currentFile]];
|
||||
int filelength = fileLength[con->fileLocation[con->currentFile]];
|
||||
|
||||
#if defined DEBUG
|
||||
fprintf(stdout, "Sending file of length %i\n", filelength);
|
||||
fprintf(stdout,
|
||||
"Current file index is: %i, maps to file index: %i\n",
|
||||
con->currentFile,
|
||||
con->fileLocation[con->currentFile]);
|
||||
#endif
|
||||
|
||||
int sentBytes = 0;
|
||||
|
||||
if (!con->headerSent[con->currentFile])
|
||||
{
|
||||
char buffer[5];
|
||||
buffer[0] = con->currentFile;
|
||||
*((int *)&buffer[1]) = filelength;
|
||||
|
||||
sentBytes = send(con->fd, buffer+con->writtenCount, 5-con->writtenCount, 0);
|
||||
|
||||
if (sentBytes == -1)
|
||||
{
|
||||
if (errno != EAGAIN && errno != EWOULDBLOCK)
|
||||
{
|
||||
con->state = ConnectionState_Complete;
|
||||
con->writtenCount = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
con->writtenCount += sentBytes;
|
||||
|
||||
assert(con->writtenCount <= 5);
|
||||
|
||||
if (con->writtenCount < 5)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
con->headerSent[con->currentFile] = true;
|
||||
con->writtenCount = 0;
|
||||
}
|
||||
|
||||
sentBytes = send(con->fd, (unsigned char *)file+con->writtenCount, filelength-con->writtenCount, 0);
|
||||
|
||||
if (sentBytes == -1)
|
||||
{
|
||||
if (errno != EAGAIN && errno != EWOULDBLOCK)
|
||||
{
|
||||
con->state = ConnectionState_Complete;
|
||||
con->writtenCount = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
con->writtenCount += sentBytes;
|
||||
|
||||
assert(con->writtenCount <= filelength);
|
||||
|
||||
if (con->writtenCount < filelength)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
con->currentFile++;
|
||||
con->writtenCount = 0;
|
||||
#if defined DEBUG
|
||||
fprintf(stdout, "Sent a file!: %s\n", fileNames[con->fileLocation[con->currentFile-1]]);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ConnectionPool::SendUnknownList( smud_connection *con )
|
||||
{
|
||||
int size = con->unknownCount+1;
|
||||
char *packet = new char[size]();
|
||||
|
||||
packet[0] = con->unknownCount;
|
||||
|
||||
#if defined DEBUG
|
||||
fprintf(stdout, "%i Files are unknown\n", con->unknownCount);
|
||||
#endif
|
||||
|
||||
int i=1;
|
||||
|
||||
for (int j=0; j<con->sentSums; j++)
|
||||
{
|
||||
if (con->shouldSend[j] == MD5Status_Unknown)
|
||||
{
|
||||
packet[i] = j;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
int sentBytes = send(con->fd, packet+con->writtenCount, size-con->writtenCount, 0);
|
||||
|
||||
if (sentBytes == -1)
|
||||
{
|
||||
if (errno != EAGAIN && errno != EWOULDBLOCK)
|
||||
{
|
||||
con->state = ConnectionState_Complete;
|
||||
con->writtenCount = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
con->writtenCount += sentBytes;
|
||||
|
||||
assert(con->writtenCount <= size);
|
||||
|
||||
if (con->writtenCount < size)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
con->state = ConnectionState_Complete;
|
||||
con->writtenCount = 0;
|
||||
#if defined DEBUG
|
||||
fprintf(stdout, "Unknowns Sent\n");
|
||||
#endif
|
||||
}
|
||||
|
@ -1,116 +0,0 @@
|
||||
#ifndef _INCLUDE_SMUD_CONNECTION_H_
|
||||
#define _INCLUDE_SMUD_CONNECTION_H_
|
||||
|
||||
#include "smud.h"
|
||||
#include <list>
|
||||
#include "poll.h"
|
||||
|
||||
#define QUERY_HEADER_SIZE 11
|
||||
#define QUERY_CONTENT_SIZE 16
|
||||
|
||||
enum ConnectionState
|
||||
{
|
||||
ConnectionState_ReadQueryHeader,
|
||||
ConnectionState_ReadQueryData,
|
||||
ConnectionState_ReplyQuery,
|
||||
ConnectionState_SendingFiles,
|
||||
ConnectionState_SendUnknownList,
|
||||
ConnectionState_Complete,
|
||||
};
|
||||
|
||||
enum QueryResult
|
||||
{
|
||||
QueryResult_Continue,
|
||||
QueryResult_Complete,
|
||||
};
|
||||
|
||||
enum MD5Status
|
||||
{
|
||||
MD5Status_Unknown,
|
||||
MD5Status_Current,
|
||||
MD5Status_NeedsUpdate,
|
||||
};
|
||||
|
||||
enum UpdateStatus
|
||||
{
|
||||
Update_Unknown = 0, /* Version wasn't recognised or version querying is unsupported */
|
||||
Update_Current = 1, /* Server is running latest version */
|
||||
Update_NewBuild = 2, /* Server is on a svn release and a newer version is available */
|
||||
Update_MinorAvailable = 3, /* Server is on a release and a minor release has superceeded it */
|
||||
Update_MajorAvailable = 4, /* Server is on a release and a major release has superceeded it */
|
||||
Update_CriticalAvailable = 5, /* A critical update has been released (security fixes etc) */
|
||||
};
|
||||
|
||||
struct smud_connection
|
||||
{
|
||||
smud_connection(int fd)
|
||||
{
|
||||
shouldSend = NULL;
|
||||
fileLocation = NULL;
|
||||
headerSent = NULL;
|
||||
sentSums = 0;
|
||||
sendCount = 0;
|
||||
currentFile = 0;
|
||||
unknownCount = 0;
|
||||
pollData.events = POLLIN;
|
||||
start = time(NULL);
|
||||
state = ConnectionState_ReadQueryHeader;
|
||||
this->fd = fd;
|
||||
pollData.fd = fd;
|
||||
buffer = NULL;
|
||||
writtenCount = 0;
|
||||
}
|
||||
|
||||
~smud_connection()
|
||||
{
|
||||
if (shouldSend != NULL)
|
||||
delete [] shouldSend;
|
||||
if (fileLocation != NULL)
|
||||
delete [] fileLocation;
|
||||
if (headerSent != NULL)
|
||||
delete [] headerSent;
|
||||
}
|
||||
|
||||
int fd; /** Socket file descriptor */
|
||||
time_t start; /** The time this connection was received (for timeouts) */
|
||||
ConnectionState state; /** How far through processing the connection we are */
|
||||
uint8_t sentSums; /** Number of MD5 Sums sent from the client */
|
||||
MD5Status *shouldSend; /** Arrays of statuses for each sum */
|
||||
int *fileLocation; /** Array of indexes into the global file list for each sum (only valid if shouldSend[i] == MD5Status_NeedsUpdate) */
|
||||
bool *headerSent; /** Has the header been sent yet for each sum? Header == file index and size */
|
||||
int sendCount; /** Number of files that need to be sent */
|
||||
int unknownCount; /** Number of files that were unknown */
|
||||
int currentFile; /** Current file being sent (index into the above 3 arrays) */
|
||||
pollfd pollData; /** Data to be passed into poll() */
|
||||
char *buffer; /** Temporary storage buffer to hold data until all of it is available */
|
||||
int writtenCount; /** Number of bytes written into the storage buffer */
|
||||
};
|
||||
|
||||
|
||||
|
||||
class ConnectionPool
|
||||
{
|
||||
public:
|
||||
ConnectionPool();
|
||||
~ConnectionPool();
|
||||
public:
|
||||
void AddConnection(int fd);
|
||||
void Process(bool *terminate);
|
||||
private:
|
||||
QueryResult ProcessConnection(smud_connection *con);
|
||||
void ReadQueryHeader(smud_connection *con);
|
||||
void ReadQueryContent(smud_connection *con);
|
||||
void ReplyQuery(smud_connection *con);
|
||||
void SendFile(smud_connection *con);
|
||||
void SendUnknownList(smud_connection *con);
|
||||
|
||||
MD5Status GetMD5UpdateStatus(const char *md5, smud_connection *con, int fileNum);
|
||||
private:
|
||||
std::list<smud_connection *> m_Links;
|
||||
std::list<smud_connection *> m_AddQueue;
|
||||
pthread_mutex_t m_AddLock;
|
||||
time_t m_timeOut;
|
||||
};
|
||||
|
||||
#endif //_INCLUDE_SMUD_CONNECTION_H_
|
||||
|
@ -1,86 +0,0 @@
|
||||
#include "smud_threads.h"
|
||||
|
||||
ThreadPool::ThreadPool()
|
||||
{
|
||||
}
|
||||
|
||||
ThreadPool::~ThreadPool()
|
||||
{
|
||||
}
|
||||
|
||||
bool ThreadPool::Start()
|
||||
{
|
||||
m_pWorker = new ThreadWorker();
|
||||
|
||||
if (!m_pWorker->Start())
|
||||
{
|
||||
delete m_pWorker;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ThreadPool::Stop()
|
||||
{
|
||||
m_pWorker->CancelAndWait();
|
||||
delete m_pWorker;
|
||||
}
|
||||
|
||||
void ThreadPool::AddConnection(int fd)
|
||||
{
|
||||
m_pWorker->AddConnection(fd);
|
||||
}
|
||||
|
||||
ThreadWorker::ThreadWorker() : m_bShouldCancel(false)
|
||||
{
|
||||
}
|
||||
|
||||
ThreadWorker::~ThreadWorker()
|
||||
{
|
||||
}
|
||||
|
||||
bool ThreadWorker::Start()
|
||||
{
|
||||
m_pPool = new ConnectionPool();
|
||||
pthread_mutex_init(&m_NotifyLock, NULL);
|
||||
pthread_cond_init(&m_Notify, NULL);
|
||||
|
||||
if (pthread_create(&m_Thread, NULL, ThreadCallback, this) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ThreadWorker::CancelAndWait()
|
||||
{
|
||||
m_bShouldCancel = true;
|
||||
|
||||
pthread_join(m_Thread, NULL);
|
||||
|
||||
pthread_cond_destroy(&m_Notify);
|
||||
pthread_mutex_destroy(&m_NotifyLock);
|
||||
|
||||
delete m_pPool;
|
||||
}
|
||||
|
||||
void ThreadWorker::AddConnection( int fd )
|
||||
{
|
||||
m_pPool->AddConnection(fd);
|
||||
}
|
||||
|
||||
void ThreadWorker::Process()
|
||||
{
|
||||
m_pPool->Process(&m_bShouldCancel);
|
||||
}
|
||||
|
||||
void *ThreadCallback(void *data)
|
||||
{
|
||||
((ThreadWorker *)data)->Process();
|
||||
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
|
@ -1,41 +0,0 @@
|
||||
#ifndef _INCLUDE_SMUD_H_
|
||||
#define _INCLUDE_SMUD_H_
|
||||
|
||||
#include "smud.h"
|
||||
#include "smud_connections.h"
|
||||
|
||||
void *ThreadCallback(void *data);
|
||||
|
||||
class ThreadWorker
|
||||
{
|
||||
public:
|
||||
ThreadWorker();
|
||||
~ThreadWorker();
|
||||
public:
|
||||
bool Start();
|
||||
void CancelAndWait();
|
||||
void AddConnection(int fd);
|
||||
void Process();
|
||||
private:
|
||||
ConnectionPool *m_pPool;
|
||||
pthread_t m_Thread;
|
||||
pthread_mutex_t m_NotifyLock;
|
||||
pthread_cond_t m_Notify;
|
||||
bool m_bShouldCancel;
|
||||
};
|
||||
|
||||
class ThreadPool
|
||||
{
|
||||
public:
|
||||
ThreadPool();
|
||||
~ThreadPool();
|
||||
public:
|
||||
void AddConnection(int fd);
|
||||
bool Start();
|
||||
void Stop();
|
||||
private:
|
||||
ThreadWorker *m_pWorker;
|
||||
};
|
||||
|
||||
#endif //_INCLUDE_SMUD_H_
|
||||
|
@ -1,30 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
FILENAME=""
|
||||
SUMSFILE=""
|
||||
|
||||
if [ -n "$1" ]
|
||||
then
|
||||
FILENAME="$1.txt"
|
||||
SUMSFILE="$1.sums"
|
||||
else
|
||||
echo "Need to specify a gamedata filename"
|
||||
exit -1
|
||||
fi
|
||||
|
||||
|
||||
if [ -s $FILENAME ]
|
||||
then
|
||||
#run ./gamedatamd5 on this file and pipe output+filename into $1.sums
|
||||
MD5=`./gamedatamd5 $FILENAME`
|
||||
#need to stop here if gamedatamd5 failed. (returns -1 and prints to stderr)
|
||||
echo "$MD5" > "$SUMSFILE"
|
||||
echo "$FILENAME" >> "$SUMSFILE"
|
||||
ln -s "$SUMSFILE" "$MD5"
|
||||
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "File $FILENAME not found!"
|
||||
|
||||
exit -1
|
Loading…
Reference in New Issue
Block a user