diff options
Diffstat (limited to '')
-rw-r--r-- | src/OSSupport/BlockingTCPLink.cpp (renamed from source/OSSupport/BlockingTCPLink.cpp) | 0 | ||||
-rw-r--r-- | src/OSSupport/BlockingTCPLink.h (renamed from source/OSSupport/BlockingTCPLink.h) | 0 | ||||
-rw-r--r-- | src/OSSupport/CriticalSection.cpp (renamed from source/OSSupport/CriticalSection.cpp) | 0 | ||||
-rw-r--r-- | src/OSSupport/CriticalSection.h (renamed from source/OSSupport/CriticalSection.h) | 0 | ||||
-rw-r--r-- | src/OSSupport/Event.cpp (renamed from source/OSSupport/Event.cpp) | 0 | ||||
-rw-r--r-- | src/OSSupport/Event.h (renamed from source/OSSupport/Event.h) | 0 | ||||
-rw-r--r-- | src/OSSupport/File.cpp | 452 | ||||
-rw-r--r-- | src/OSSupport/File.h | 144 | ||||
-rw-r--r-- | src/OSSupport/GZipFile.cpp (renamed from source/OSSupport/GZipFile.cpp) | 0 | ||||
-rw-r--r-- | src/OSSupport/GZipFile.h | 52 | ||||
-rw-r--r-- | src/OSSupport/IsThread.cpp | 172 | ||||
-rw-r--r-- | src/OSSupport/IsThread.h | 100 | ||||
-rw-r--r-- | src/OSSupport/ListenThread.cpp | 238 | ||||
-rw-r--r-- | src/OSSupport/ListenThread.h (renamed from source/OSSupport/ListenThread.h) | 0 | ||||
-rw-r--r-- | src/OSSupport/Semaphore.cpp (renamed from source/OSSupport/Semaphore.cpp) | 0 | ||||
-rw-r--r-- | src/OSSupport/Semaphore.h (renamed from source/OSSupport/Semaphore.h) | 0 | ||||
-rw-r--r-- | src/OSSupport/Sleep.cpp (renamed from source/OSSupport/Sleep.cpp) | 0 | ||||
-rw-r--r-- | src/OSSupport/Sleep.h (renamed from source/OSSupport/Sleep.h) | 0 | ||||
-rw-r--r-- | src/OSSupport/Socket.cpp | 393 | ||||
-rw-r--r-- | src/OSSupport/Socket.h | 102 | ||||
-rw-r--r-- | src/OSSupport/SocketThreads.cpp (renamed from source/OSSupport/SocketThreads.cpp) | 0 | ||||
-rw-r--r-- | src/OSSupport/SocketThreads.h (renamed from source/OSSupport/SocketThreads.h) | 0 | ||||
-rw-r--r-- | src/OSSupport/Thread.cpp (renamed from source/OSSupport/Thread.cpp) | 0 | ||||
-rw-r--r-- | src/OSSupport/Thread.h (renamed from source/OSSupport/Thread.h) | 0 | ||||
-rw-r--r-- | src/OSSupport/Timer.cpp (renamed from source/OSSupport/Timer.cpp) | 0 | ||||
-rw-r--r-- | src/OSSupport/Timer.h (renamed from source/OSSupport/Timer.h) | 0 |
26 files changed, 1653 insertions, 0 deletions
diff --git a/source/OSSupport/BlockingTCPLink.cpp b/src/OSSupport/BlockingTCPLink.cpp index 55454a4b5..55454a4b5 100644 --- a/source/OSSupport/BlockingTCPLink.cpp +++ b/src/OSSupport/BlockingTCPLink.cpp diff --git a/source/OSSupport/BlockingTCPLink.h b/src/OSSupport/BlockingTCPLink.h index cb5f9e3f4..cb5f9e3f4 100644 --- a/source/OSSupport/BlockingTCPLink.h +++ b/src/OSSupport/BlockingTCPLink.h diff --git a/source/OSSupport/CriticalSection.cpp b/src/OSSupport/CriticalSection.cpp index bda97e3a1..bda97e3a1 100644 --- a/source/OSSupport/CriticalSection.cpp +++ b/src/OSSupport/CriticalSection.cpp diff --git a/source/OSSupport/CriticalSection.h b/src/OSSupport/CriticalSection.h index 1bfe81439..1bfe81439 100644 --- a/source/OSSupport/CriticalSection.h +++ b/src/OSSupport/CriticalSection.h diff --git a/source/OSSupport/Event.cpp b/src/OSSupport/Event.cpp index cbacbba17..cbacbba17 100644 --- a/source/OSSupport/Event.cpp +++ b/src/OSSupport/Event.cpp diff --git a/source/OSSupport/Event.h b/src/OSSupport/Event.h index 71f418c0c..71f418c0c 100644 --- a/source/OSSupport/Event.h +++ b/src/OSSupport/Event.h diff --git a/src/OSSupport/File.cpp b/src/OSSupport/File.cpp new file mode 100644 index 000000000..9f7c0d439 --- /dev/null +++ b/src/OSSupport/File.cpp @@ -0,0 +1,452 @@ + +// cFile.cpp + +// Implements the cFile class providing an OS-independent abstraction of a file. + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "File.h" +#include <fstream> + + + + + +cFile::cFile(void) : + #ifdef USE_STDIO_FILE + m_File(NULL) + #else + m_File(INVALID_HANDLE_VALUE) + #endif // USE_STDIO_FILE +{ + // Nothing needed yet +} + + + + + +cFile::cFile(const AString & iFileName, eMode iMode) : + #ifdef USE_STDIO_FILE + m_File(NULL) + #else + m_File(INVALID_HANDLE_VALUE) + #endif // USE_STDIO_FILE +{ + Open(iFileName, iMode); +} + + + + + +cFile::~cFile() +{ + if (IsOpen()) + { + Close(); + } +} + + + + + +bool cFile::Open(const AString & iFileName, eMode iMode) +{ + ASSERT(!IsOpen()); // You should close the file before opening another one + + if (IsOpen()) + { + Close(); + } + + const char * Mode = NULL; + switch (iMode) + { + case fmRead: Mode = "rb"; break; + case fmWrite: Mode = "wb"; break; + case fmReadWrite: Mode = "rb+"; break; + default: + { + ASSERT(!"Unhandled file mode"); + return false; + } + } + m_File = fopen( (FILE_IO_PREFIX + iFileName).c_str(), Mode); + if ((m_File == NULL) && (iMode == fmReadWrite)) + { + // Fix for MS not following C spec, opening "a" mode files for writing at the end only + // The file open operation has been tried with "read update", fails if file not found + // So now we know either the file doesn't exist or we don't have rights, no need to worry about file contents. + // Simply re-open for read-writing, erasing existing contents: + m_File = fopen( (FILE_IO_PREFIX + iFileName).c_str(), "wb+"); + } + return (m_File != NULL); +} + + + + + +void cFile::Close(void) +{ + if (!IsOpen()) + { + // Closing an unopened file is a legal nop + return; + } + + fclose(m_File); + m_File = NULL; +} + + + + + +bool cFile::IsOpen(void) const +{ + return (m_File != NULL); +} + + + + + +bool cFile::IsEOF(void) const +{ + ASSERT(IsOpen()); + + if (!IsOpen()) + { + // Unopened files behave as at EOF + return true; + } + + return (feof(m_File) != 0); +} + + + + + +int cFile::Read (void * iBuffer, int iNumBytes) +{ + ASSERT(IsOpen()); + + if (!IsOpen()) + { + return -1; + } + + return fread(iBuffer, 1, iNumBytes, m_File); // fread() returns the portion of Count parameter actually read, so we need to send iNumBytes as Count +} + + + + + +int cFile::Write(const void * iBuffer, int iNumBytes) +{ + ASSERT(IsOpen()); + + if (!IsOpen()) + { + return -1; + } + + int res = fwrite(iBuffer, 1, iNumBytes, m_File); // fwrite() returns the portion of Count parameter actually written, so we need to send iNumBytes as Count + return res; +} + + + + + +int cFile::Seek (int iPosition) +{ + ASSERT(IsOpen()); + + if (!IsOpen()) + { + return -1; + } + + if (fseek(m_File, iPosition, SEEK_SET) != 0) + { + return -1; + } + return ftell(m_File); +} + + + + + + +int cFile::Tell (void) const +{ + ASSERT(IsOpen()); + + if (!IsOpen()) + { + return -1; + } + + return ftell(m_File); +} + + + + + +int cFile::GetSize(void) const +{ + ASSERT(IsOpen()); + + if (!IsOpen()) + { + return -1; + } + + int CurPos = ftell(m_File); + if (CurPos < 0) + { + return -1; + } + if (fseek(m_File, 0, SEEK_END) != 0) + { + return -1; + } + int res = ftell(m_File); + if (fseek(m_File, CurPos, SEEK_SET) != 0) + { + return -1; + } + return res; +} + + + + + +int cFile::ReadRestOfFile(AString & a_Contents) +{ + ASSERT(IsOpen()); + + if (!IsOpen()) + { + return -1; + } + + int DataSize = GetSize() - Tell(); + + // HACK: This depends on the internal knowledge that AString's data() function returns the internal buffer directly + a_Contents.assign(DataSize, '\0'); + return Read((void *)a_Contents.data(), DataSize); +} + + + + + +bool cFile::Exists(const AString & a_FileName) +{ + cFile test(a_FileName, fmRead); + return test.IsOpen(); +} + + + + + +bool cFile::Delete(const AString & a_FileName) +{ + return (remove(a_FileName.c_str()) == 0); +} + + + + + +bool cFile::Rename(const AString & a_OrigFileName, const AString & a_NewFileName) +{ + return (rename(a_OrigFileName.c_str(), a_NewFileName.c_str()) == 0); +} + + + + + +bool cFile::Copy(const AString & a_SrcFileName, const AString & a_DstFileName) +{ + #ifdef _WIN32 + return (CopyFile(a_SrcFileName.c_str(), a_DstFileName.c_str(), true) != 0); + #else + // Other OSs don't have a direct CopyFile equivalent, do it the harder way: + std::ifstream src(a_SrcFileName.c_str(), std::ios::binary); + std::ofstream dst(a_DstFileName.c_str(), std::ios::binary); + if (dst.good()) + { + dst << src.rdbuf(); + return true; + } + else + { + return false; + } + #endif +} + + + + + +bool cFile::IsFolder(const AString & a_Path) +{ + #ifdef _WIN32 + DWORD FileAttrib = GetFileAttributes(a_Path.c_str()); + return ((FileAttrib != INVALID_FILE_ATTRIBUTES) && ((FileAttrib & FILE_ATTRIBUTE_DIRECTORY) != 0)); + #else + struct stat st; + return ((stat(a_Path.c_str(), &st) == 0) && S_ISDIR(st.st_mode)); + #endif +} + + + + + +bool cFile::IsFile(const AString & a_Path) +{ + #ifdef _WIN32 + DWORD FileAttrib = GetFileAttributes(a_Path.c_str()); + return ((FileAttrib != INVALID_FILE_ATTRIBUTES) && ((FileAttrib & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE)) == 0)); + #else + struct stat st; + return ((stat(a_Path.c_str(), &st) == 0) && S_ISREG(st.st_mode)); + #endif +} + + + + + +int cFile::GetSize(const AString & a_FileName) +{ + struct stat st; + if (stat(a_FileName.c_str(), &st) == 0) + { + return st.st_size; + } + return -1; +} + + + + + +bool cFile::CreateFolder(const AString & a_FolderPath) +{ + #ifdef _WIN32 + return (CreateDirectory(a_FolderPath.c_str(), NULL) != 0); + #else + return (mkdir(a_FolderPath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) == 0); + #endif +} + + + + + +AStringVector cFile::GetFolderContents(const AString & a_Folder) +{ + AStringVector AllFiles; + + #ifdef _WIN32 + + // If the folder name doesn't contain the terminating slash / backslash, add it: + AString FileFilter = a_Folder; + if ( + !FileFilter.empty() && + (FileFilter[FileFilter.length() - 1] != '\\') && + (FileFilter[FileFilter.length() - 1] != '/') + ) + { + FileFilter.push_back('\\'); + } + + // Find all files / folders: + FileFilter.append("*.*"); + HANDLE hFind; + WIN32_FIND_DATA FindFileData; + if ((hFind = FindFirstFile(FileFilter.c_str(), &FindFileData)) != INVALID_HANDLE_VALUE) + { + do + { + AllFiles.push_back(FindFileData.cFileName); + } while (FindNextFile(hFind, &FindFileData)); + FindClose(hFind); + } + + #else // _WIN32 + + DIR * dp; + struct dirent *dirp; + AString Folder = a_Folder; + if (Folder.empty()) + { + Folder = "."; + } + if ((dp = opendir(Folder.c_str())) == NULL) + { + LOGERROR("Error (%i) opening directory \"%s\"\n", errno, Folder.c_str()); + } + else + { + while ((dirp = readdir(dp)) != NULL) + { + AllFiles.push_back(dirp->d_name); + } + closedir(dp); + } + + #endif // else _WIN32 + + return AllFiles; +} + + + + + +AString cFile::ReadWholeFile(const AString & a_FileName) +{ + cFile f; + if (!f.Open(a_FileName, fmRead)) + { + return ""; + } + AString Contents; + f.ReadRestOfFile(Contents); + return Contents; +} + + + + + +int cFile::Printf(const char * a_Fmt, ...) +{ + AString buf; + va_list args; + va_start(args, a_Fmt); + AppendVPrintf(buf, a_Fmt, args); + va_end(args); + return Write(buf.c_str(), buf.length()); +} + + + + diff --git a/src/OSSupport/File.h b/src/OSSupport/File.h new file mode 100644 index 000000000..01663a229 --- /dev/null +++ b/src/OSSupport/File.h @@ -0,0 +1,144 @@ + +// cFile.h + +// Interfaces to the cFile class providing an OS-independent abstraction of a file. + +/* +The object is optimized towards binary reads. +The object has no multithreading locks, don't use from multiple threads! +Usage: +1, Construct a cFile instance (no-param constructor) +2, Open a file using Open(), check return value for success +3, Read / write +4, Destroy the instance + +-- OR -- + +1, Construct a cFile instance opening the file (filename-param constructor) +2, Check if the file was opened using IsOpen() +3, Read / write +4, Destroy the instance +*/ + + + + + +#pragma once + + + + + +#ifndef _WIN32 + #define USE_STDIO_FILE +#endif // _WIN32 + +// DEBUG: +#define USE_STDIO_FILE + + + + + +// tolua_begin + +class cFile +{ +public: + + // tolua_end + + #ifdef _WIN32 + static const char PathSeparator = '\\'; + #else + static const char PathSeparator = '/'; + #endif + + /// The mode in which to open the file + enum eMode + { + fmRead, // Read-only. If the file doesn't exist, object will not be valid + fmWrite, // Write-only. If the file already exists, it will be overwritten + fmReadWrite // Read/write. If the file already exists, it will be left intact; writing will overwrite the data from the beginning + } ; + + /// Simple constructor - creates an unopened file object, use Open() to open / create a real file + cFile(void); + + /// Constructs and opens / creates the file specified, use IsOpen() to check for success + cFile(const AString & iFileName, eMode iMode); + + /// Auto-closes the file, if open + ~cFile(); + + bool Open(const AString & iFileName, eMode iMode); + void Close(void); + bool IsOpen(void) const; + bool IsEOF(void) const; + + /// Reads up to iNumBytes bytes into iBuffer, returns the number of bytes actually read, or -1 on failure; asserts if not open + int Read (void * iBuffer, int iNumBytes); + + /// Writes up to iNumBytes bytes from iBuffer, returns the number of bytes actually written, or -1 on failure; asserts if not open + int Write(const void * iBuffer, int iNumBytes); + + /// Seeks to iPosition bytes from file start, returns old position or -1 for failure; asserts if not open + int Seek (int iPosition); + + /// Returns the current position (bytes from file start) or -1 for failure; asserts if not open + int Tell (void) const; + + /// Returns the size of file, in bytes, or -1 for failure; asserts if not open + int GetSize(void) const; + + /// Reads the file from current position till EOF into an AString; returns the number of bytes read or -1 for error + int ReadRestOfFile(AString & a_Contents); + + // tolua_begin + + /// Returns true if the file specified exists + static bool Exists(const AString & a_FileName); + + /// Deletes a file, returns true if successful + static bool Delete(const AString & a_FileName); + + /// Renames a file or folder, returns true if successful. May fail if dest already exists (libc-dependant)! + static bool Rename(const AString & a_OrigPath, const AString & a_NewPath); + + /// Copies a file, returns true if successful. + static bool Copy(const AString & a_SrcFileName, const AString & a_DstFileName); + + /// Returns true if the specified path is a folder + static bool IsFolder(const AString & a_Path); + + /// Returns true if the specified path is a regular file + static bool IsFile(const AString & a_Path); + + /// Returns the size of the file, or a negative number on error + static int GetSize(const AString & a_FileName); + + /// Creates a new folder with the specified name. Returns true if successful. Path may be relative or absolute + static bool CreateFolder(const AString & a_FolderPath); + + /// Returns the entire contents of the specified file as a string. Returns empty string on error. + static AString ReadWholeFile(const AString & a_FileName); + + // tolua_end + + /// Returns the list of all items in the specified folder (files, folders, nix pipes, whatever's there). + static AStringVector GetFolderContents(const AString & a_Folder); // Exported in ManualBindings.cpp + + int Printf(const char * a_Fmt, ...); + +private: + #ifdef USE_STDIO_FILE + FILE * m_File; + #else + HANDLE m_File; + #endif +} ; // tolua_export + + + + diff --git a/source/OSSupport/GZipFile.cpp b/src/OSSupport/GZipFile.cpp index cbf6be6c4..cbf6be6c4 100644 --- a/source/OSSupport/GZipFile.cpp +++ b/src/OSSupport/GZipFile.cpp diff --git a/src/OSSupport/GZipFile.h b/src/OSSupport/GZipFile.h new file mode 100644 index 000000000..dfb4e8c31 --- /dev/null +++ b/src/OSSupport/GZipFile.h @@ -0,0 +1,52 @@ + +// GZipFile.h + +// Declares the cGZipFile class representing a RAII wrapper over zlib's GZip file routines + + + + + +#pragma once + +#include "zlib/zlib.h" + + + + + +class cGZipFile +{ +public: + enum eMode + { + fmRead, // Read-only. If the file doesn't exist, object will not be valid + fmWrite, // Write-only. If the file already exists, it will be overwritten + } ; + + cGZipFile(void); + ~cGZipFile(); + + /// Opens the file. Returns true if successful. Fails if a file has already been opened through this object. + bool Open(const AString & a_FileName, eMode a_Mode); + + /// Closes the file, flushing all buffers. This object may be then reused for a different file and / or mode + void Close(void); + + /// Reads the rest of the file and decompresses it into a_Contents. Returns the number of decompressed bytes, <0 for error + int ReadRestOfFile(AString & a_Contents); + + /// Writes a_Contents into file, compressing it along the way. Returns true if successful. Multiple writes are supported. + bool Write(const AString & a_Contents) { return Write(a_Contents.data(), (int)(a_Contents.size())); } + + bool Write(const char * a_Data, int a_Size); + +protected: + gzFile m_File; + eMode m_Mode; +} ; + + + + + diff --git a/src/OSSupport/IsThread.cpp b/src/OSSupport/IsThread.cpp new file mode 100644 index 000000000..4da9f9949 --- /dev/null +++ b/src/OSSupport/IsThread.cpp @@ -0,0 +1,172 @@ + +// IsThread.cpp + +// Implements the cIsThread class representing an OS-independent wrapper for a class that implements a thread. +// This class will eventually suupersede the old cThread class + +#include "Globals.h" + +#include "IsThread.h" + + + + + +// When in MSVC, the debugger provides "thread naming" by catching special exceptions. Interface here: +#if defined(_MSC_VER) && defined(_DEBUG) +// +// Usage: SetThreadName (-1, "MainThread"); +// + +static void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName) +{ + struct + { + DWORD dwType; // must be 0x1000 + LPCSTR szName; // pointer to name (in user addr space) + DWORD dwThreadID; // thread ID (-1=caller thread) + DWORD dwFlags; // reserved for future use, must be zero + } info; + + info.dwType = 0x1000; + info.szName = szThreadName; + info.dwThreadID = dwThreadID; + info.dwFlags = 0; + + __try + { + RaiseException(0x406D1388, 0, sizeof(info) / sizeof(DWORD), (DWORD *)&info); + } + __except(EXCEPTION_CONTINUE_EXECUTION) + { + } +} +#endif // _MSC_VER && _DEBUG + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cIsThread: + +cIsThread::cIsThread(const AString & iThreadName) : + m_ThreadName(iThreadName), + m_ShouldTerminate(false), + m_Handle(NULL_HANDLE) +{ +} + + + + + +cIsThread::~cIsThread() +{ + m_ShouldTerminate = true; + Wait(); +} + + + + + +bool cIsThread::Start(void) +{ + ASSERT(m_Handle == NULL_HANDLE); // Has already started one thread? + #ifdef _WIN32 + // Create the thread suspended, so that the mHandle variable is valid in the thread procedure + DWORD ThreadID = 0; + m_Handle = CreateThread(NULL, 0, thrExecute, this, CREATE_SUSPENDED, &ThreadID); + if (m_Handle == NULL) + { + LOGERROR("ERROR: Could not create thread \"%s\", GLE = %d!", m_ThreadName.c_str(), GetLastError()); + return false; + } + ResumeThread(m_Handle); + + #if defined(_DEBUG) && defined(_MSC_VER) + // Thread naming is available only in MSVC + if (!m_ThreadName.empty()) + { + SetThreadName(ThreadID, m_ThreadName.c_str()); + } + #endif // _DEBUG and _MSC_VER + + #else // _WIN32 + if (pthread_create(&m_Handle, NULL, thrExecute, this)) + { + LOGERROR("ERROR: Could not create thread \"%s\", !", m_ThreadName.c_str()); + return false; + } + #endif // else _WIN32 + + return true; +} + + + + + +void cIsThread::Stop(void) +{ + if (m_Handle == NULL_HANDLE) + { + return; + } + m_ShouldTerminate = true; + Wait(); +} + + + + + +bool cIsThread::Wait(void) +{ + if (m_Handle == NULL) + { + return true; + } + + #ifdef LOGD // ProtoProxy doesn't have LOGD + LOGD("Waiting for thread %s to finish", m_ThreadName.c_str()); + #endif // LOGD + + #ifdef _WIN32 + int res = WaitForSingleObject(m_Handle, INFINITE); + m_Handle = NULL; + + #ifdef LOGD // ProtoProxy doesn't have LOGD + LOGD("Thread %s finished", m_ThreadName.c_str()); + #endif // LOGD + + return (res == WAIT_OBJECT_0); + #else // _WIN32 + int res = pthread_join(m_Handle, NULL); + m_Handle = NULL; + + #ifdef LOGD // ProtoProxy doesn't have LOGD + LOGD("Thread %s finished", m_ThreadName.c_str()); + #endif // LOGD + + return (res == 0); + #endif // else _WIN32 +} + + + + + +unsigned long cIsThread::GetCurrentID(void) +{ + #ifdef _WIN32 + return (unsigned long) GetCurrentThreadId(); + #else + return (unsigned long) pthread_self(); + #endif +} + + + + diff --git a/src/OSSupport/IsThread.h b/src/OSSupport/IsThread.h new file mode 100644 index 000000000..b8784ea33 --- /dev/null +++ b/src/OSSupport/IsThread.h @@ -0,0 +1,100 @@ + +// IsThread.h + +// Interfaces to the cIsThread class representing an OS-independent wrapper for a class that implements a thread. +// This class will eventually suupersede the old cThread class + +/* +Usage: +To have a new thread, declare a class descending from cIsClass. +Then override its Execute() method to provide your thread processing. +In the descending class' constructor call the Start() method to start the thread once you're finished with initialization. +*/ + + + + + +#pragma once +#ifndef CISTHREAD_H_INCLUDED +#define CISTHREAD_H_INCLUDED + + + + + +class cIsThread +{ +protected: + /// This is the main thread entrypoint + virtual void Execute(void) = 0; + + /// The overriden Execute() method should check this value periodically and terminate if this is true + volatile bool m_ShouldTerminate; + +public: + cIsThread(const AString & iThreadName); + ~cIsThread(); + + /// Starts the thread; returns without waiting for the actual start + bool Start(void); + + /// Signals the thread to terminate and waits until it's finished + void Stop(void); + + /// Waits for the thread to finish. Doesn't signalize the ShouldTerminate flag + bool Wait(void); + + /// Returns the OS-dependent thread ID for the caller's thread + static unsigned long GetCurrentID(void); + +protected: + AString m_ThreadName; + + // Value used for "no handle": + #ifdef _WIN32 + #define NULL_HANDLE NULL + #else + #define NULL_HANDLE 0 + #endif + + #ifdef _WIN32 + + HANDLE m_Handle; + + static DWORD_PTR __stdcall thrExecute(LPVOID a_Param) + { + // Create a window so that the thread can be identified by 3rd party tools: + HWND IdentificationWnd = CreateWindow("STATIC", ((cIsThread *)a_Param)->m_ThreadName.c_str(), 0, 0, 0, 0, WS_OVERLAPPED, NULL, NULL, NULL, NULL); + + // Run the thread: + ((cIsThread *)a_Param)->Execute(); + + // Destroy the identification window: + DestroyWindow(IdentificationWnd); + + return 0; + } + + #else // _WIN32 + + pthread_t m_Handle; + + static void * thrExecute(void * a_Param) + { + ((cIsThread *)a_Param)->Execute(); + return NULL; + } + + #endif // else _WIN32 +} ; + + + + + +#endif // CISTHREAD_H_INCLUDED + + + + diff --git a/src/OSSupport/ListenThread.cpp b/src/OSSupport/ListenThread.cpp new file mode 100644 index 000000000..ba3198764 --- /dev/null +++ b/src/OSSupport/ListenThread.cpp @@ -0,0 +1,238 @@ + +// ListenThread.cpp + +// Implements the cListenThread class representing the thread that listens for client connections + +#include "Globals.h" +#include "ListenThread.h" + + + + + +cListenThread::cListenThread(cCallback & a_Callback, cSocket::eFamily a_Family, const AString & a_ServiceName) : + super(Printf("ListenThread %s", a_ServiceName.c_str())), + m_Callback(a_Callback), + m_Family(a_Family), + m_ShouldReuseAddr(false), + m_ServiceName(a_ServiceName) +{ +} + + + + + +cListenThread::~cListenThread() +{ + Stop(); +} + + + + + +bool cListenThread::Initialize(const AString & a_PortsString) +{ + ASSERT(m_Sockets.empty()); // Not yet started + + if (!CreateSockets(a_PortsString)) + { + return false; + } + + return true; +} + + + + + +bool cListenThread::Start(void) +{ + if (m_Sockets.empty()) + { + // There are no sockets listening, either forgotten to initialize or the user specified no listening ports + // Report as successful, though + return true; + } + return super::Start(); +} + + + + + +void cListenThread::Stop(void) +{ + if (m_Sockets.empty()) + { + // No sockets means no thread was running in the first place + return; + } + + m_ShouldTerminate = true; + + // Close one socket to wake the thread up from the select() call + m_Sockets[0].CloseSocket(); + + // Wait for the thread to finish + super::Wait(); + + // Close all the listening sockets: + for (cSockets::iterator itr = m_Sockets.begin() + 1, end = m_Sockets.end(); itr != end; ++itr) + { + itr->CloseSocket(); + } // for itr - m_Sockets[] + m_Sockets.clear(); +} + + + + + +void cListenThread::SetReuseAddr(bool a_Reuse) +{ + ASSERT(m_Sockets.empty()); // Must not have been Initialize()d yet + + m_ShouldReuseAddr = a_Reuse; +} + + + + + +bool cListenThread::CreateSockets(const AString & a_PortsString) +{ + AStringVector Ports = StringSplitAndTrim(a_PortsString, ","); + + if (Ports.empty()) + { + return false; + } + + AString FamilyStr = m_ServiceName; + switch (m_Family) + { + case cSocket::IPv4: FamilyStr.append(" IPv4"); break; + case cSocket::IPv6: FamilyStr.append(" IPv6"); break; + default: + { + ASSERT(!"Unknown address family"); + break; + } + } + + for (AStringVector::const_iterator itr = Ports.begin(), end = Ports.end(); itr != end; ++itr) + { + int Port = atoi(itr->c_str()); + if ((Port <= 0) || (Port > 65535)) + { + LOGWARNING("%s: Invalid port specified: \"%s\".", FamilyStr.c_str(), itr->c_str()); + continue; + } + m_Sockets.push_back(cSocket::CreateSocket(m_Family)); + if (!m_Sockets.back().IsValid()) + { + LOGWARNING("%s: Cannot create listening socket for port %d: \"%s\"", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str()); + m_Sockets.pop_back(); + continue; + } + + if (m_ShouldReuseAddr) + { + if (!m_Sockets.back().SetReuseAddress()) + { + LOG("%s: Port %d cannot reuse addr, syscall failed: \"%s\".", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str()); + } + } + + // Bind to port: + bool res = false; + switch (m_Family) + { + case cSocket::IPv4: res = m_Sockets.back().BindToAnyIPv4(Port); break; + case cSocket::IPv6: res = m_Sockets.back().BindToAnyIPv6(Port); break; + default: + { + ASSERT(!"Unknown address family"); + res = false; + } + } + if (!res) + { + LOGWARNING("%s: Cannot bind port %d: \"%s\".", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str()); + m_Sockets.pop_back(); + continue; + } + + if (!m_Sockets.back().Listen()) + { + LOGWARNING("%s: Cannot listen on port %d: \"%s\".", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str()); + m_Sockets.pop_back(); + continue; + } + + LOGINFO("%s: Port %d is open for connections", FamilyStr.c_str(), Port); + } // for itr - Ports[] + + return !(m_Sockets.empty()); +} + + + + + +void cListenThread::Execute(void) +{ + if (m_Sockets.empty()) + { + LOGD("Empty cListenThread, ending thread now."); + return; + } + + // Find the highest socket number: + cSocket::xSocket Highest = m_Sockets[0].GetSocket(); + for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr) + { + if (itr->GetSocket() > Highest) + { + Highest = itr->GetSocket(); + } + } // for itr - m_Sockets[] + + while (!m_ShouldTerminate) + { + // Put all sockets into a FD set: + fd_set fdRead; + FD_ZERO(&fdRead); + for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr) + { + FD_SET(itr->GetSocket(), &fdRead); + } // for itr - m_Sockets[] + + timeval tv; // On Linux select() doesn't seem to wake up when socket is closed, so let's kinda busy-wait: + tv.tv_sec = 1; + tv.tv_usec = 0; + if (select(Highest + 1, &fdRead, NULL, NULL, &tv) == -1) + { + LOG("select(R) call failed in cListenThread: \"%s\"", cSocket::GetLastErrorString().c_str()); + continue; + } + for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr) + { + if (itr->IsValid() && FD_ISSET(itr->GetSocket(), &fdRead)) + { + cSocket Client = (m_Family == cSocket::IPv4) ? itr->AcceptIPv4() : itr->AcceptIPv6(); + if (Client.IsValid()) + { + m_Callback.OnConnectionAccepted(Client); + } + } + } // for itr - m_Sockets[] + } // while (!m_ShouldTerminate) +} + + + + diff --git a/source/OSSupport/ListenThread.h b/src/OSSupport/ListenThread.h index 4e337d814..4e337d814 100644 --- a/source/OSSupport/ListenThread.h +++ b/src/OSSupport/ListenThread.h diff --git a/source/OSSupport/Semaphore.cpp b/src/OSSupport/Semaphore.cpp index 468de6858..468de6858 100644 --- a/source/OSSupport/Semaphore.cpp +++ b/src/OSSupport/Semaphore.cpp diff --git a/source/OSSupport/Semaphore.h b/src/OSSupport/Semaphore.h index fbe8907f1..fbe8907f1 100644 --- a/source/OSSupport/Semaphore.h +++ b/src/OSSupport/Semaphore.h diff --git a/source/OSSupport/Sleep.cpp b/src/OSSupport/Sleep.cpp index 70fb06b40..70fb06b40 100644 --- a/source/OSSupport/Sleep.cpp +++ b/src/OSSupport/Sleep.cpp diff --git a/source/OSSupport/Sleep.h b/src/OSSupport/Sleep.h index 5298c15da..5298c15da 100644 --- a/source/OSSupport/Sleep.h +++ b/src/OSSupport/Sleep.h diff --git a/src/OSSupport/Socket.cpp b/src/OSSupport/Socket.cpp new file mode 100644 index 000000000..f25f800c2 --- /dev/null +++ b/src/OSSupport/Socket.cpp @@ -0,0 +1,393 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Socket.h" + +#ifndef _WIN32 + #include <netdb.h> + #include <unistd.h> + #include <arpa/inet.h> //inet_ntoa() +#else + #define socklen_t int +#endif + + + + + +cSocket::cSocket(xSocket a_Socket) + : m_Socket(a_Socket) +{ +} + + + + + +cSocket::~cSocket() +{ + // Do NOT close the socket; this class is an API wrapper, not a RAII! +} + + + + + +cSocket::operator cSocket::xSocket() const +{ + return m_Socket; +} + + + + + +cSocket::xSocket cSocket::GetSocket() const +{ + return m_Socket; +} + + + + + +bool cSocket::IsValidSocket(cSocket::xSocket a_Socket) +{ + #ifdef _WIN32 + return (a_Socket != INVALID_SOCKET); + #else // _WIN32 + return (a_Socket >= 0); + #endif // else _WIN32 +} + + + + + +void cSocket::CloseSocket() +{ + #ifdef _WIN32 + + closesocket(m_Socket); + + #else // _WIN32 + + if (shutdown(m_Socket, SHUT_RDWR) != 0)//SD_BOTH); + { + LOGWARN("Error on shutting down socket %d (%s): %s", m_Socket, m_IPString.c_str(), GetLastErrorString().c_str()); + } + if (close(m_Socket) != 0) + { + LOGWARN("Error closing socket %d (%s): %s", m_Socket, m_IPString.c_str(), GetLastErrorString().c_str()); + } + + #endif // else _WIN32 + + // Invalidate the socket so that this object can be re-used for another connection + m_Socket = INVALID_SOCKET; +} + + + + + +AString cSocket::GetErrorString( int a_ErrNo ) +{ + char buffer[ 1024 ]; + AString Out; + + #ifdef _WIN32 + + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, a_ErrNo, 0, buffer, ARRAYCOUNT(buffer), NULL); + Printf(Out, "%d: %s", a_ErrNo, buffer); + if (!Out.empty() && (Out[Out.length() - 1] == '\n')) + { + Out.erase(Out.length() - 2); + } + return Out; + + #else // _WIN32 + + // According to http://linux.die.net/man/3/strerror_r there are two versions of strerror_r(): + + #if ( _GNU_SOURCE ) && !defined(ANDROID_NDK) // GNU version of strerror_r() + + char * res = strerror_r( errno, buffer, ARRAYCOUNT(buffer) ); + if( res != NULL ) + { + Printf(Out, "%d: %s", a_ErrNo, res); + return Out; + } + + #else // XSI version of strerror_r(): + + int res = strerror_r( errno, buffer, ARRAYCOUNT(buffer) ); + if( res == 0 ) + { + Printf(Out, "%d: %s", a_ErrNo, buffer); + return Out; + } + + #endif // strerror_r() version + + else + { + Printf(Out, "Error %d while getting error string for error #%d!", errno, a_ErrNo); + return Out; + } + + #endif // else _WIN32 +} + + + + +int cSocket::GetLastError() +{ +#ifdef _WIN32 + return WSAGetLastError(); +#else + return errno; +#endif +} + + + + + +bool cSocket::SetReuseAddress(void) +{ + #if defined(_WIN32) || defined(ANDROID_NDK) + char yes = 1; + #else + int yes = 1; + #endif + return (setsockopt(m_Socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == 0); +} + + + + + +int cSocket::WSAStartup(void) +{ +#ifdef _WIN32 + WSADATA wsaData; + memset(&wsaData, 0, sizeof(wsaData)); + return ::WSAStartup(MAKEWORD(2, 2),&wsaData); +#else + return 0; +#endif +} + + + + + +cSocket cSocket::CreateSocket(eFamily a_Family) +{ + return socket((int)a_Family, SOCK_STREAM, 0); +} + + + + + +bool cSocket::BindToAnyIPv4(unsigned short a_Port) +{ + sockaddr_in local; + memset(&local, 0, sizeof(local)); + + local.sin_family = AF_INET; + local.sin_port = htons((u_short)a_Port); + + return (bind(m_Socket, (sockaddr *)&local, sizeof(local)) == 0); +} + + + + + +bool cSocket::BindToAnyIPv6(unsigned short a_Port) +{ + sockaddr_in6 local; + memset(&local, 0, sizeof(local)); + + local.sin6_family = AF_INET6; + local.sin6_port = htons((u_short)a_Port); + + return (bind(m_Socket, (sockaddr *)&local, sizeof(local)) == 0); +} + + + + + +bool cSocket::BindToLocalhostIPv4(unsigned short a_Port) +{ + sockaddr_in local; + memset(&local, 0, sizeof(local)); + + local.sin_family = AF_INET;; + local.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + local.sin_port = htons((u_short)a_Port); + + return (bind(m_Socket, (sockaddr*)&local, sizeof(local)) == 0); +} + + + + + +bool cSocket::Listen(int a_Backlog) +{ + return (listen(m_Socket, a_Backlog) == 0); +} + + + + + +cSocket cSocket::AcceptIPv4(void) +{ + sockaddr_in from; + socklen_t fromlen = sizeof(from); + + cSocket SClient = accept(m_Socket, (sockaddr *)&from, &fromlen); + + if (SClient.IsValid() && (from.sin_addr.s_addr != 0)) // Get IP in string form + { + SClient.m_IPString = inet_ntoa(from.sin_addr); + } + return SClient; +} + + + + + +cSocket cSocket::AcceptIPv6(void) +{ + sockaddr_in6 from; + socklen_t fromlen = sizeof(from); + + cSocket SClient = accept(m_Socket, (sockaddr *)&from, &fromlen); + + // Get IP in string form: + if (SClient.IsValid()) + { + #if defined(_WIN32) + // Windows XP doesn't have inet_ntop, so we need to improvise. And MSVC has different headers than GCC + #ifdef _MSC_VER + // MSVC version + Printf(SClient.m_IPString, "%x:%x:%x:%x:%x:%x:%x:%x", + from.sin6_addr.u.Word[0], + from.sin6_addr.u.Word[1], + from.sin6_addr.u.Word[2], + from.sin6_addr.u.Word[3], + from.sin6_addr.u.Word[4], + from.sin6_addr.u.Word[5], + from.sin6_addr.u.Word[6], + from.sin6_addr.u.Word[7] + ); + #else // _MSC_VER + // MinGW + Printf(SClient.m_IPString, "%x:%x:%x:%x:%x:%x:%x:%x", + from.sin6_addr.s6_addr16[0], + from.sin6_addr.s6_addr16[1], + from.sin6_addr.s6_addr16[2], + from.sin6_addr.s6_addr16[3], + from.sin6_addr.s6_addr16[4], + from.sin6_addr.s6_addr16[5], + from.sin6_addr.s6_addr16[6], + from.sin6_addr.s6_addr16[7] + ); + #endif // else _MSC_VER + #else + char buffer[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &(from.sin6_addr), buffer, sizeof(buffer)); + SClient.m_IPString.assign(buffer); + #endif // _WIN32 + } + return SClient; +} + + + + + +bool cSocket::ConnectToLocalhostIPv4(unsigned short a_Port) +{ + sockaddr_in server; + server.sin_family = AF_INET; + server.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + server.sin_port = htons(a_Port); + return (connect(m_Socket, (sockaddr *)&server, sizeof(server)) == 0); +} + + + + + +bool cSocket::ConnectIPv4(const AString & a_HostNameOrAddr, unsigned short a_Port) +{ + // First try IP Address string to hostent conversion, because it's faster + unsigned long addr = inet_addr(a_HostNameOrAddr.c_str()); + if (addr == INADDR_NONE) + { + // It is not an IP Address string, but rather a regular hostname, resolve: + hostent * hp = gethostbyname(a_HostNameOrAddr.c_str()); + if (hp == NULL) + { + LOGWARNING("%s: Could not resolve hostname \"%s\"", __FUNCTION__, a_HostNameOrAddr.c_str()); + CloseSocket(); + return false; + } + addr = *((unsigned long*)hp->h_addr); + } + + sockaddr_in server; + server.sin_addr.s_addr = addr; + server.sin_family = AF_INET; + server.sin_port = htons((unsigned short)a_Port); + return (connect(m_Socket, (sockaddr *)&server, sizeof(server)) == 0); +} + + + + + +int cSocket::Receive(char* a_Buffer, unsigned int a_Length, unsigned int a_Flags) +{ + return recv(m_Socket, a_Buffer, a_Length, a_Flags); +} + + + + + +int cSocket::Send(const char * a_Buffer, unsigned int a_Length) +{ + return send(m_Socket, a_Buffer, a_Length, 0); +} + + + + + +unsigned short cSocket::GetPort(void) const +{ + ASSERT(IsValid()); + + sockaddr_in Addr; + socklen_t AddrSize = sizeof(Addr); + if (getsockname(m_Socket, (sockaddr *)&Addr, &AddrSize) != 0) + { + return 0; + } + return ntohs(Addr.sin_port); +} + + + + diff --git a/src/OSSupport/Socket.h b/src/OSSupport/Socket.h new file mode 100644 index 000000000..81bfd28fc --- /dev/null +++ b/src/OSSupport/Socket.h @@ -0,0 +1,102 @@ + +#pragma once + + + + + +class cSocket +{ +public: + enum eFamily + { + IPv4 = AF_INET, + IPv6 = AF_INET6, + } ; + +#ifdef _WIN32 + typedef SOCKET xSocket; +#else + typedef int xSocket; + static const int INVALID_SOCKET = -1; +#endif + + cSocket(void) : m_Socket(INVALID_SOCKET) {} + cSocket(xSocket a_Socket); + ~cSocket(); + + bool IsValid(void) const { return IsValidSocket(m_Socket); } + void CloseSocket(void); + + operator xSocket(void) const; + xSocket GetSocket(void) const; + + bool operator == (const cSocket & a_Other) {return m_Socket == a_Other.m_Socket; } + + void SetSocket(xSocket a_Socket); + + /// Sets the address-reuse socket flag; returns true on success + bool SetReuseAddress(void); + + /// Initializes the network stack. Returns 0 on success, or another number as an error code. + static int WSAStartup(void); + + static AString GetErrorString(int a_ErrNo); + static int GetLastError(); + static AString GetLastErrorString(void) + { + return GetErrorString(GetLastError()); + } + + /// Creates a new socket of the specified address family + static cSocket CreateSocket(eFamily a_Family); + + inline static bool IsSocketError(int a_ReturnedValue) + { + #ifdef _WIN32 + return (a_ReturnedValue == SOCKET_ERROR || a_ReturnedValue == 0); + #else + return (a_ReturnedValue <= 0); + #endif + } + + static bool IsValidSocket(xSocket a_Socket); + + static const unsigned short ANY_PORT = 0; // When given to Bind() functions, they will find a free port + static const int DEFAULT_BACKLOG = 10; + + /// Binds to the specified port on "any" interface (0.0.0.0). Returns true if successful. + bool BindToAnyIPv4(unsigned short a_Port); + + /// Binds to the specified port on "any" interface (::/128). Returns true if successful. + bool BindToAnyIPv6(unsigned short a_Port); + + /// Binds to the specified port on localhost interface (127.0.0.1) through IPv4. Returns true if successful. + bool BindToLocalhostIPv4(unsigned short a_Port); + + /// Sets the socket to listen for incoming connections. Returns true if successful. + bool Listen(int a_Backlog = DEFAULT_BACKLOG); + + /// Accepts an IPv4 incoming connection. Blocks if none available. + cSocket AcceptIPv4(void); + + /// Accepts an IPv6 incoming connection. Blocks if none available. + cSocket AcceptIPv6(void); + + /// Connects to a localhost socket on the specified port using IPv4; returns true if successful. + bool ConnectToLocalhostIPv4(unsigned short a_Port); + + /// Connects to the specified host or string IP address and port, using IPv4. Returns true if successful. + bool ConnectIPv4(const AString & a_HostNameOrAddr, unsigned short a_Port); + + int Receive(char * a_Buffer, unsigned int a_Length, unsigned int a_Flags); + int Send (const char * a_Buffer, unsigned int a_Length); + + unsigned short GetPort(void) const; // Returns 0 on failure + + const AString & GetIPString(void) const { return m_IPString; } + +private: + xSocket m_Socket; + AString m_IPString; +};
\ No newline at end of file diff --git a/source/OSSupport/SocketThreads.cpp b/src/OSSupport/SocketThreads.cpp index 3e505616c..3e505616c 100644 --- a/source/OSSupport/SocketThreads.cpp +++ b/src/OSSupport/SocketThreads.cpp diff --git a/source/OSSupport/SocketThreads.h b/src/OSSupport/SocketThreads.h index ecbac3aeb..ecbac3aeb 100644 --- a/source/OSSupport/SocketThreads.h +++ b/src/OSSupport/SocketThreads.h diff --git a/source/OSSupport/Thread.cpp b/src/OSSupport/Thread.cpp index 3df75f0e7..3df75f0e7 100644 --- a/source/OSSupport/Thread.cpp +++ b/src/OSSupport/Thread.cpp diff --git a/source/OSSupport/Thread.h b/src/OSSupport/Thread.h index 3c9316424..3c9316424 100644 --- a/source/OSSupport/Thread.h +++ b/src/OSSupport/Thread.h diff --git a/source/OSSupport/Timer.cpp b/src/OSSupport/Timer.cpp index ed16f9e3a..ed16f9e3a 100644 --- a/source/OSSupport/Timer.cpp +++ b/src/OSSupport/Timer.cpp diff --git a/source/OSSupport/Timer.h b/src/OSSupport/Timer.h index a059daa41..a059daa41 100644 --- a/source/OSSupport/Timer.h +++ b/src/OSSupport/Timer.h |