From f61c33538b0a7a9833c4946a765cc6c98cc759b7 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Wed, 26 Jun 2013 07:56:00 -0700 Subject: [PATCH] Add missing native BSD support for Beast --- SConstruct | 9 + .../Builds/VisualStudio2012/beast.vcxproj | 16 + .../VisualStudio2012/beast.vcxproj.filters | 12 + .../beast/modules/beast_core/beast_core.cpp | 7 + .../native/beast_BasicNativeHeaders.h | 9 + .../beast_core/native/beast_bsd_Files.cpp | 373 ++++++++++++++ .../beast_core/native/beast_bsd_Network.cpp | 461 ++++++++++++++++++ .../native/beast_bsd_SystemStats.cpp | 346 +++++++++++++ .../beast_core/native/beast_bsd_Threads.cpp | 85 ++++ .../beast_core/system/beast_StandardHeader.h | 4 +- .../beast_core/system/beast_SystemStats.cpp | 6 +- .../beast_core/system/beast_SystemStats.h | 13 +- 12 files changed, 1330 insertions(+), 11 deletions(-) create mode 100644 Subtrees/beast/modules/beast_core/native/beast_bsd_Files.cpp create mode 100644 Subtrees/beast/modules/beast_core/native/beast_bsd_Network.cpp create mode 100644 Subtrees/beast/modules/beast_core/native/beast_bsd_SystemStats.cpp create mode 100644 Subtrees/beast/modules/beast_core/native/beast_bsd_Threads.cpp diff --git a/SConstruct b/SConstruct index b63f94d168..6d272413a7 100644 --- a/SConstruct +++ b/SConstruct @@ -52,6 +52,14 @@ env.ParseConfig('pkg-config --cflags --libs openssl') # Use protobuf env.ParseConfig('pkg-config --cflags --libs protobuf') +# Beast uses kvm on FreeBSD +if FreeBSD: + env.Append ( + LIBS = [ + 'kvm' + ] + ) + # The required version of boost is documented in the README file. # # We whitelist platforms where the non -mt version is linked with pthreads. @@ -59,6 +67,7 @@ env.ParseConfig('pkg-config --cflags --libs protobuf') # If a threading library is included the platform can be whitelisted. # # FreeBSD and Ubuntu non-mt libs do link with pthreads. + if FreeBSD or Ubuntu: env.Append( LIBS = [ diff --git a/Subtrees/beast/Builds/VisualStudio2012/beast.vcxproj b/Subtrees/beast/Builds/VisualStudio2012/beast.vcxproj index 04af470a2b..5872be2984 100644 --- a/Subtrees/beast/Builds/VisualStudio2012/beast.vcxproj +++ b/Subtrees/beast/Builds/VisualStudio2012/beast.vcxproj @@ -419,6 +419,22 @@ true true + + true + true + + + true + true + + + true + true + + + true + true + true true diff --git a/Subtrees/beast/Builds/VisualStudio2012/beast.vcxproj.filters b/Subtrees/beast/Builds/VisualStudio2012/beast.vcxproj.filters index f2f88ba0bc..5a57fbb7e9 100644 --- a/Subtrees/beast/Builds/VisualStudio2012/beast.vcxproj.filters +++ b/Subtrees/beast/Builds/VisualStudio2012/beast.vcxproj.filters @@ -952,5 +952,17 @@ beast_basics\threads + + beast_core\native + + + beast_core\native + + + beast_core\native + + + beast_core\native + \ No newline at end of file diff --git a/Subtrees/beast/modules/beast_core/beast_core.cpp b/Subtrees/beast/modules/beast_core/beast_core.cpp index 0514b14c54..4bf563e2a5 100644 --- a/Subtrees/beast/modules/beast_core/beast_core.cpp +++ b/Subtrees/beast/modules/beast_core/beast_core.cpp @@ -201,6 +201,13 @@ namespace beast #include "native/beast_linux_SystemStats.cpp" #include "native/beast_linux_Threads.cpp" +//============================================================================== +#elif BEAST_BSD +#include "native/beast_bsd_Files.cpp" +#include "native/beast_bsd_Network.cpp" +#include "native/beast_bsd_SystemStats.cpp" +#include "native/beast_bsd_Threads.cpp" + //============================================================================== #elif BEAST_ANDROID #include "native/beast_android_Files.cpp" diff --git a/Subtrees/beast/modules/beast_core/native/beast_BasicNativeHeaders.h b/Subtrees/beast/modules/beast_core/native/beast_BasicNativeHeaders.h index 5af5a11d72..21249d82f7 100644 --- a/Subtrees/beast/modules/beast_core/native/beast_BasicNativeHeaders.h +++ b/Subtrees/beast/modules/beast_core/native/beast_BasicNativeHeaders.h @@ -188,8 +188,17 @@ #if BEAST_BSD #include + #include + #include + #include #include #include + #include + #include + + // This has to be in the global namespace + extern char** environ; + #else #include #include diff --git a/Subtrees/beast/modules/beast_core/native/beast_bsd_Files.cpp b/Subtrees/beast/modules/beast_core/native/beast_bsd_Files.cpp new file mode 100644 index 0000000000..6487f80fb3 --- /dev/null +++ b/Subtrees/beast/modules/beast_core/native/beast_bsd_Files.cpp @@ -0,0 +1,373 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Portions of this file are from JUCE. + Copyright (c) 2013 - Raw Material Software Ltd. + Please visit http://www.juce.com + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +enum +{ + U_ISOFS_SUPER_MAGIC = 0x9660, // linux/iso_fs.h + U_MSDOS_SUPER_MAGIC = 0x4d44, // linux/msdos_fs.h + U_NFS_SUPER_MAGIC = 0x6969, // linux/nfs_fs.h + U_SMB_SUPER_MAGIC = 0x517B // linux/smb_fs.h +}; + +//============================================================================== +bool File::copyInternal (const File& dest) const +{ + FileInputStream in (*this); + + if (dest.deleteFile()) + { + { + FileOutputStream out (dest); + + if (out.failedToOpen()) + return false; + + if (out.writeFromInputStream (in, -1) == getSize()) + return true; + } + + dest.deleteFile(); + } + + return false; +} + +void File::findFileSystemRoots (Array& destArray) +{ + destArray.add (File ("/")); +} + +//============================================================================== +bool File::isOnCDRomDrive() const +{ + struct statfs buf; + + return statfs (getFullPathName().toUTF8(), &buf) == 0 + && buf.f_type == (short) U_ISOFS_SUPER_MAGIC; +} + +bool File::isOnHardDisk() const +{ + struct statfs buf; + + if (statfs (getFullPathName().toUTF8(), &buf) == 0) + { + switch (buf.f_type) + { + case U_ISOFS_SUPER_MAGIC: // CD-ROM + case U_MSDOS_SUPER_MAGIC: // Probably floppy (but could be mounted FAT filesystem) + case U_NFS_SUPER_MAGIC: // Network NFS + case U_SMB_SUPER_MAGIC: // Network Samba + return false; + + default: + // Assume anything else is a hard-disk (but note it could + // be a RAM disk. There isn't a good way of determining + // this for sure) + return true; + } + } + + // Assume so if this fails for some reason + return true; +} + +bool File::isOnRemovableDrive() const +{ + bassertfalse; // xxx not implemented for linux! + return false; +} + +bool File::isHidden() const +{ + return getFileName().startsWithChar ('.'); +} + +//============================================================================== +namespace +{ + File beast_readlink (const String& file, const File& defaultFile) + { + const size_t size = 8192; + HeapBlock buffer; + buffer.malloc (size + 4); + + const size_t numBytes = readlink (file.toUTF8(), buffer, size); + + if (numBytes > 0 && numBytes <= size) + return File (file).getSiblingFile (String::fromUTF8 (buffer, (int) numBytes)); + + return defaultFile; + } +} + +File File::getLinkedTarget() const +{ + return beast_readlink (getFullPathName().toUTF8(), *this); +} + +//============================================================================== +static File resolveXDGFolder (const char* const type, const char* const fallbackFolder) +{ + StringArray confLines; + File ("~/.config/user-dirs.dirs").readLines (confLines); + + for (int i = 0; i < confLines.size(); ++i) + { + const String line (confLines[i].trimStart()); + + if (line.startsWith (type)) + { + // eg. resolve XDG_MUSIC_DIR="$HOME/Music" to /home/user/Music + const File f (line.replace ("$HOME", File ("~").getFullPathName()) + .fromFirstOccurrenceOf ("=", false, false) + .trim().unquoted()); + + if (f.isDirectory()) + return f; + } + } + + return File (fallbackFolder); +} + +const char* const* beast_argv = nullptr; +int beast_argc = 0; + +File File::getSpecialLocation (const SpecialLocationType type) +{ + switch (type) + { + case userHomeDirectory: + { + const char* homeDir = getenv ("HOME"); + + if (homeDir == nullptr) + { + struct passwd* const pw = getpwuid (getuid()); + if (pw != nullptr) + homeDir = pw->pw_dir; + } + + return File (CharPointer_UTF8 (homeDir)); + } + + case userDocumentsDirectory: return resolveXDGFolder ("XDG_DOCUMENTS_DIR", "~"); + case userMusicDirectory: return resolveXDGFolder ("XDG_MUSIC_DIR", "~"); + case userMoviesDirectory: return resolveXDGFolder ("XDG_VIDEOS_DIR", "~"); + case userPicturesDirectory: return resolveXDGFolder ("XDG_PICTURES_DIR", "~"); + case userDesktopDirectory: return resolveXDGFolder ("XDG_DESKTOP_DIR", "~/Desktop"); + case userApplicationDataDirectory: return File ("~"); + case commonApplicationDataDirectory: return File ("/var"); + case globalApplicationsDirectory: return File ("/usr"); + + case tempDirectory: + { + File tmp ("/var/tmp"); + + if (! tmp.isDirectory()) + { + tmp = "/tmp"; + + if (! tmp.isDirectory()) + tmp = File::getCurrentWorkingDirectory(); + } + + return tmp; + } + + case invokedExecutableFile: + if (beast_argv != nullptr && beast_argc > 0) + return File (CharPointer_UTF8 (beast_argv[0])); + // deliberate fall-through... + + case currentExecutableFile: + case currentApplicationFile: + return beast_getExecutableFile(); + + case hostApplicationPath: + return beast_readlink ("/proc/self/exe", beast_getExecutableFile()); + + default: + bassertfalse; // unknown type? + break; + } + + return File::nonexistent; +} + +//============================================================================== +String File::getVersion() const +{ + return String::empty; // xxx not yet implemented +} + +//============================================================================== +bool File::moveToTrash() const +{ + if (! exists()) + return true; + + File trashCan ("~/.Trash"); + + if (! trashCan.isDirectory()) + trashCan = "~/.local/share/Trash/files"; + + if (! trashCan.isDirectory()) + return false; + + return moveFileTo (trashCan.getNonexistentChildFile (getFileNameWithoutExtension(), + getFileExtension())); +} + +//============================================================================== +class DirectoryIterator::NativeIterator::Pimpl +{ +public: + Pimpl (const File& directory, const String& wildCard_) + : parentDir (File::addTrailingSeparator (directory.getFullPathName())), + wildCard (wildCard_), + dir (opendir (directory.getFullPathName().toUTF8())) + { + } + + ~Pimpl() + { + if (dir != nullptr) + closedir (dir); + } + + bool next (String& filenameFound, + bool* const isDir, bool* const isHidden, int64* const fileSize, + Time* const modTime, Time* const creationTime, bool* const isReadOnly) + { + if (dir != nullptr) + { + const char* wildcardUTF8 = nullptr; + + for (;;) + { + struct dirent* const de = readdir (dir); + + if (de == nullptr) + break; + + if (wildcardUTF8 == nullptr) + wildcardUTF8 = wildCard.toUTF8(); + + if (fnmatch (wildcardUTF8, de->d_name, FNM_CASEFOLD) == 0) + { + filenameFound = CharPointer_UTF8 (de->d_name); + + updateStatInfoForFile (parentDir + filenameFound, isDir, fileSize, modTime, creationTime, isReadOnly); + + if (isHidden != nullptr) + *isHidden = filenameFound.startsWithChar ('.'); + + return true; + } + } + } + + return false; + } + +private: + String parentDir, wildCard; + DIR* dir; + + BEAST_DECLARE_NON_COPYABLE (Pimpl) +}; + +DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildCard) + : pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildCard)) +{ +} + +DirectoryIterator::NativeIterator::~NativeIterator() +{ +} + +bool DirectoryIterator::NativeIterator::next (String& filenameFound, + bool* const isDir, bool* const isHidden, int64* const fileSize, + Time* const modTime, Time* const creationTime, bool* const isReadOnly) +{ + return pimpl->next (filenameFound, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly); +} + + +//============================================================================== +static bool isFileExecutable (const String& filename) +{ + beast_statStruct info; + + return beast_stat (filename, info) + && S_ISREG (info.st_mode) + && access (filename.toUTF8(), X_OK) == 0; +} + +bool Process::openDocument (const String& fileName, const String& parameters) +{ + String cmdString (fileName.replace (" ", "\\ ",false)); + cmdString << " " << parameters; + + if (URL::isProbablyAWebsiteURL (fileName) + || cmdString.startsWithIgnoreCase ("file:") + || URL::isProbablyAnEmailAddress (fileName) + || File::createFileWithoutCheckingPath (fileName).isDirectory() + || ! isFileExecutable (fileName)) + { + // create a command that tries to launch a bunch of likely browsers + const char* const browserNames[] = { "xdg-open", "/etc/alternatives/x-www-browser", "firefox", "mozilla", + "google-chrome", "chromium-browser", "opera", "konqueror" }; + StringArray cmdLines; + + for (int i = 0; i < numElementsInArray (browserNames); ++i) + cmdLines.add (String (browserNames[i]) + " " + cmdString.trim().quoted()); + + cmdString = cmdLines.joinIntoString (" || "); + } + + const char* const argv[4] = { "/bin/sh", "-c", cmdString.toUTF8(), 0 }; + + const int cpid = fork(); + + if (cpid == 0) + { + setsid(); + + // Child process + execve (argv[0], (char**) argv, environ); + exit (0); + } + + return cpid >= 0; +} + +void File::revealToUser() const +{ + if (isDirectory()) + startAsProcess(); + else if (getParentDirectory().exists()) + getParentDirectory().startAsProcess(); +} diff --git a/Subtrees/beast/modules/beast_core/native/beast_bsd_Network.cpp b/Subtrees/beast/modules/beast_core/native/beast_bsd_Network.cpp new file mode 100644 index 0000000000..9cc852ca9e --- /dev/null +++ b/Subtrees/beast/modules/beast_core/native/beast_bsd_Network.cpp @@ -0,0 +1,461 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Portions of this file are from JUCE. + Copyright (c) 2013 - Raw Material Software Ltd. + Please visit http://www.juce.com + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +void MACAddress::findAllAddresses (Array& result) +{ +#if 1 + bassertfalse; // VFALCO TODO Implement for FreeBSD +#else + const int s = socket (AF_INET, SOCK_DGRAM, 0); + if (s != -1) + { + char buf [1024]; + struct ifconf ifc; + ifc.ifc_len = sizeof (buf); + ifc.ifc_buf = buf; + ioctl (s, SIOCGIFCONF, &ifc); + + for (unsigned int i = 0; i < ifc.ifc_len / sizeof (struct ifreq); ++i) + { + struct ifreq ifr; + strcpy (ifr.ifr_name, ifc.ifc_req[i].ifr_name); + + if (ioctl (s, SIOCGIFFLAGS, &ifr) == 0 + && (ifr.ifr_flags & IFF_LOOPBACK) == 0 + && ioctl (s, SIOCGIFHWADDR, &ifr) == 0) + { + result.addIfNotAlreadyThere (MACAddress ((const uint8*) ifr.ifr_hwaddr.sa_data)); + } + } + + close (s); + } +#endif +} + + +bool Process::openEmailWithAttachments (const String& /* targetEmailAddress */, + const String& /* emailSubject */, + const String& /* bodyText */, + const StringArray& /* filesToAttach */) +{ + bassertfalse; // xxx todo + + return false; +} + + +//============================================================================== +class WebInputStream : public InputStream +{ +public: + WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, + URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, + const String& headers_, int timeOutMs_, StringPairArray* responseHeaders) + : socketHandle (-1), levelsOfRedirection (0), + address (address_), headers (headers_), postData (postData_), position (0), + finished (false), isPost (isPost_), timeOutMs (timeOutMs_) + { + createConnection (progressCallback, progressCallbackContext); + + if (responseHeaders != nullptr && ! isError()) + { + for (int i = 0; i < headerLines.size(); ++i) + { + const String& headersEntry = headerLines[i]; + const String key (headersEntry.upToFirstOccurrenceOf (": ", false, false)); + const String value (headersEntry.fromFirstOccurrenceOf (": ", false, false)); + const String previousValue ((*responseHeaders) [key]); + responseHeaders->set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); + } + } + } + + ~WebInputStream() + { + closeSocket(); + } + + //============================================================================== + bool isError() const { return socketHandle < 0; } + bool isExhausted() { return finished; } + int64 getPosition() { return position; } + + int64 getTotalLength() + { + //xxx to do + return -1; + } + + int read (void* buffer, int bytesToRead) + { + if (finished || isError()) + return 0; + + fd_set readbits; + FD_ZERO (&readbits); + FD_SET (socketHandle, &readbits); + + struct timeval tv; + tv.tv_sec = bmax (1, timeOutMs / 1000); + tv.tv_usec = 0; + + if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) + return 0; // (timeout) + + const int bytesRead = bmax (0, (int) recv (socketHandle, buffer, bytesToRead, MSG_WAITALL)); + if (bytesRead == 0) + finished = true; + position += bytesRead; + return bytesRead; + } + + bool setPosition (int64 wantedPos) + { + if (isError()) + return false; + + if (wantedPos != position) + { + finished = false; + + if (wantedPos < position) + { + closeSocket(); + position = 0; + createConnection (0, 0); + } + + skipNextBytes (wantedPos - position); + } + + return true; + } + + //============================================================================== +private: + int socketHandle, levelsOfRedirection; + StringArray headerLines; + String address, headers; + MemoryBlock postData; + int64 position; + bool finished; + const bool isPost; + const int timeOutMs; + + void closeSocket() + { + if (socketHandle >= 0) + close (socketHandle); + + socketHandle = -1; + levelsOfRedirection = 0; + } + + void createConnection (URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext) + { + closeSocket(); + + uint32 timeOutTime = Time::getMillisecondCounter(); + + if (timeOutMs == 0) + timeOutTime += 60000; + else if (timeOutMs < 0) + timeOutTime = 0xffffffff; + else + timeOutTime += timeOutMs; + + String hostName, hostPath; + int hostPort; + if (! decomposeURL (address, hostName, hostPath, hostPort)) + return; + + String serverName, proxyName, proxyPath; + int proxyPort = 0; + int port = 0; + + const String proxyURL (getenv ("http_proxy")); + if (proxyURL.startsWithIgnoreCase ("http://")) + { + if (! decomposeURL (proxyURL, proxyName, proxyPath, proxyPort)) + return; + + serverName = proxyName; + port = proxyPort; + } + else + { + serverName = hostName; + port = hostPort; + } + + struct addrinfo hints; + zerostruct (hints); + + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICSERV; + + struct addrinfo* result = nullptr; + if (getaddrinfo (serverName.toUTF8(), String (port).toUTF8(), &hints, &result) != 0 || result == 0) + return; + + socketHandle = socket (result->ai_family, result->ai_socktype, 0); + + if (socketHandle == -1) + { + freeaddrinfo (result); + return; + } + + int receiveBufferSize = 16384; + setsockopt (socketHandle, SOL_SOCKET, SO_RCVBUF, (char*) &receiveBufferSize, sizeof (receiveBufferSize)); + setsockopt (socketHandle, SOL_SOCKET, SO_KEEPALIVE, 0, 0); + + #if BEAST_MAC + setsockopt (socketHandle, SOL_SOCKET, SO_NOSIGPIPE, 0, 0); + #endif + + if (connect (socketHandle, result->ai_addr, result->ai_addrlen) == -1) + { + closeSocket(); + freeaddrinfo (result); + return; + } + + freeaddrinfo (result); + + { + const MemoryBlock requestHeader (createRequestHeader (hostName, hostPort, proxyName, proxyPort, + hostPath, address, headers, postData, isPost)); + + if (! sendHeader (socketHandle, requestHeader, timeOutTime, progressCallback, progressCallbackContext)) + { + closeSocket(); + return; + } + } + + String responseHeader (readResponse (socketHandle, timeOutTime)); + + if (responseHeader.isNotEmpty()) + { + headerLines = StringArray::fromLines (responseHeader); + + const int statusCode = responseHeader.fromFirstOccurrenceOf (" ", false, false) + .substring (0, 3).getIntValue(); + + //int contentLength = findHeaderItem (lines, "Content-Length:").getIntValue(); + //bool isChunked = findHeaderItem (lines, "Transfer-Encoding:").equalsIgnoreCase ("chunked"); + + String location (findHeaderItem (headerLines, "Location:")); + + if (statusCode >= 300 && statusCode < 400 && location.isNotEmpty()) + { + if (! location.startsWithIgnoreCase ("http://")) + location = "http://" + location; + + if (++levelsOfRedirection <= 3) + { + address = location; + createConnection (progressCallback, progressCallbackContext); + return; + } + } + else + { + levelsOfRedirection = 0; + return; + } + } + + closeSocket(); + } + + //============================================================================== + static String readResponse (const int socketHandle, const uint32 timeOutTime) + { + int bytesRead = 0, numConsecutiveLFs = 0; + MemoryBlock buffer (1024, true); + + while (numConsecutiveLFs < 2 && bytesRead < 32768 + && Time::getMillisecondCounter() <= timeOutTime) + { + fd_set readbits; + FD_ZERO (&readbits); + FD_SET (socketHandle, &readbits); + + struct timeval tv; + tv.tv_sec = bmax (1, (int) (timeOutTime - Time::getMillisecondCounter()) / 1000); + tv.tv_usec = 0; + + if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) + return String::empty; // (timeout) + + buffer.ensureSize (bytesRead + 8, true); + char* const dest = (char*) buffer.getData() + bytesRead; + + if (recv (socketHandle, dest, 1, 0) == -1) + return String::empty; + + const char lastByte = *dest; + ++bytesRead; + + if (lastByte == '\n') + ++numConsecutiveLFs; + else if (lastByte != '\r') + numConsecutiveLFs = 0; + } + + const String header (CharPointer_UTF8 ((const char*) buffer.getData())); + + if (header.startsWithIgnoreCase ("HTTP/")) + return header.trimEnd(); + + return String::empty; + } + + static void writeValueIfNotPresent (MemoryOutputStream& dest, const String& headers, const String& key, const String& value) + { + if (! headers.containsIgnoreCase (key)) + dest << "\r\n" << key << ' ' << value; + } + + static void writeHost (MemoryOutputStream& dest, const bool isPost, const String& path, const String& host, const int port) + { + dest << (isPost ? "POST " : "GET ") << path << " HTTP/1.0\r\nHost: " << host; + + if (port > 0) + dest << ':' << port; + } + + static MemoryBlock createRequestHeader (const String& hostName, const int hostPort, + const String& proxyName, const int proxyPort, + const String& hostPath, const String& originalURL, + const String& userHeaders, const MemoryBlock& postData, + const bool isPost) + { + MemoryOutputStream header; + + if (proxyName.isEmpty()) + writeHost (header, isPost, hostPath, hostName, hostPort); + else + writeHost (header, isPost, originalURL, proxyName, proxyPort); + + writeValueIfNotPresent (header, userHeaders, "User-Agent:", "BEAST/" BEAST_STRINGIFY(BEAST_MAJOR_VERSION) + "." BEAST_STRINGIFY(BEAST_MINOR_VERSION) + "." BEAST_STRINGIFY(BEAST_BUILDNUMBER)); + writeValueIfNotPresent (header, userHeaders, "Connection:", "Close"); + + if (isPost) + writeValueIfNotPresent (header, userHeaders, "Content-Length:", String ((int) postData.getSize())); + + header << "\r\n" << userHeaders + << "\r\n" << postData; + + return header.getMemoryBlock(); + } + + static bool sendHeader (int socketHandle, const MemoryBlock& requestHeader, const uint32 timeOutTime, + URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext) + { + size_t totalHeaderSent = 0; + + while (totalHeaderSent < requestHeader.getSize()) + { + if (Time::getMillisecondCounter() > timeOutTime) + return false; + + const int numToSend = bmin (1024, (int) (requestHeader.getSize() - totalHeaderSent)); + + if (send (socketHandle, static_cast (requestHeader.getData()) + totalHeaderSent, numToSend, 0) != numToSend) + return false; + + totalHeaderSent += numToSend; + + if (progressCallback != nullptr && ! progressCallback (progressCallbackContext, totalHeaderSent, requestHeader.getSize())) + return false; + } + + return true; + } + + static bool decomposeURL (const String& url, String& host, String& path, int& port) + { + if (! url.startsWithIgnoreCase ("http://")) + return false; + + const int nextSlash = url.indexOfChar (7, '/'); + int nextColon = url.indexOfChar (7, ':'); + if (nextColon > nextSlash && nextSlash > 0) + nextColon = -1; + + if (nextColon >= 0) + { + host = url.substring (7, nextColon); + + if (nextSlash >= 0) + port = url.substring (nextColon + 1, nextSlash).getIntValue(); + else + port = url.substring (nextColon + 1).getIntValue(); + } + else + { + port = 80; + + if (nextSlash >= 0) + host = url.substring (7, nextSlash); + else + host = url.substring (7); + } + + if (nextSlash >= 0) + path = url.substring (nextSlash); + else + path = "/"; + + return true; + } + + static String findHeaderItem (const StringArray& lines, const String& itemName) + { + for (int i = 0; i < lines.size(); ++i) + if (lines[i].startsWithIgnoreCase (itemName)) + return lines[i].substring (itemName.length()).trim(); + + return String::empty; + } + + BEAST_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream) +}; + +InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData, + OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, + const String& headers, const int timeOutMs, StringPairArray* responseHeaders) +{ + ScopedPointer wi (new WebInputStream (address, isPost, postData, + progressCallback, progressCallbackContext, + headers, timeOutMs, responseHeaders)); + + return wi->isError() ? nullptr : wi.release(); +} diff --git a/Subtrees/beast/modules/beast_core/native/beast_bsd_SystemStats.cpp b/Subtrees/beast/modules/beast_core/native/beast_bsd_SystemStats.cpp new file mode 100644 index 0000000000..613ca15296 --- /dev/null +++ b/Subtrees/beast/modules/beast_core/native/beast_bsd_SystemStats.cpp @@ -0,0 +1,346 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Portions of this file are from JUCE. + Copyright (c) 2013 - Raw Material Software Ltd. + Please visit http://www.juce.com + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +// sysinfo() from sysinfo-bsd +// https://code.google.com/p/sysinfo-bsd/ +/* + * Copyright (C) 2010 Kostas Petrikas, All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name(s) of the author(s) may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#define SI_LOAD_SHIFT 16 +struct sysinfo { + long uptime; /* Seconds since boot */ + unsigned long loads[3]; /* 1, 5, and 15 minute load averages */ + unsigned long totalram; /* Total usable main memory size */ + unsigned long freeram; /* Available memory size */ + unsigned long sharedram; /* Amount of shared memory */ + unsigned long bufferram; /* Memory used by buffers */ + unsigned long totalswap; /* Total swap space size */ + unsigned long freeswap; /* swap space still available */ + unsigned short procs; /* Number of current processes */ + unsigned short pad; /* leaving this for linux compatability */ + unsigned long totalhigh; /* Total high memory size */ + unsigned long freehigh; /* Available high memory size */ + unsigned int mem_unit; /* Memory unit size in bytes */ + char _f[20-2*sizeof(long)-sizeof(int)]; /* leaving this for linux compatability */ +}; + +#define NLOADS 3 +#define UNIT_S 1024 /*Kb*/ +#define R_IGNORE -1 + +/*the macros*/ +#define R_ERROR(_EC) {if(_EC > R_IGNORE)errno = _EC; return -1;} +#define GETSYSCTL(name, var) getsysctl((char*)name, &(var), sizeof(var)) +#define PAGE_2_UNIT(_PAGE) (((double)_PAGE * page_s) / UNIT_S) + +/*sysctl wrapper*/ +static int getsysctl(char *name, void *ptr, size_t len){ + size_t nlen = len; + if (sysctlbyname(name, ptr, &nlen, NULL, 0) == -1) + return -1; + + if (nlen != len) + return -1; + + return 0; +} + +int sysinfo(struct sysinfo *info){ + kvm_t *kvmh; + double load_avg[NLOADS]; + int page_s = getpagesize(); + + if (info == NULL) + R_ERROR(EFAULT); + + memset(info, 0, sizeof(struct sysinfo)); + info -> mem_unit = UNIT_S; + + /*kvm init*/ + if ((kvmh = kvm_open(NULL, "/dev/null", "/dev/null", + O_RDONLY, "kvm_open")) == NULL) + R_ERROR(0); + + /*load averages*/ + if (kvm_getloadavg(kvmh, load_avg, NLOADS) == -1) + R_ERROR(0); + + info -> loads[0] = (u_long)((float)load_avg[0] * USHRT_MAX); + info -> loads[1] = (u_long)((float)load_avg[1] * USHRT_MAX); + info -> loads[2] = (u_long)((float)load_avg[2] * USHRT_MAX); + + /*swap space*/ + struct kvm_swap k_swap; + + if (kvm_getswapinfo(kvmh, &k_swap, 1, 0) == -1) + R_ERROR(0); + + info -> totalswap = + (u_long)PAGE_2_UNIT(k_swap.ksw_total); + info -> freeswap = info -> totalswap - + (u_long)PAGE_2_UNIT(k_swap.ksw_used); + + /*processes*/ + int n_procs; + + if (kvm_getprocs(kvmh, KERN_PROC_ALL, 0, &n_procs) == NULL) + R_ERROR(0); + + info -> procs = (u_short)n_procs; + + /*end of kvm session*/ + if (kvm_close(kvmh) == -1) + R_ERROR(0); + + /*uptime*/ + struct timespec ts; + + if (clock_gettime(CLOCK_UPTIME, &ts) == -1) + R_ERROR(R_IGNORE); + + info -> uptime = (long)ts.tv_sec; + + /*ram*/ + int total_pages, + free_pages, + active_pages, + inactive_pages; + u_long shmmax; + + if (GETSYSCTL("vm.stats.vm.v_page_count", total_pages) == -1) + R_ERROR(R_IGNORE); + if (GETSYSCTL("vm.stats.vm.v_free_count", free_pages) == -1) + R_ERROR(R_IGNORE); + if (GETSYSCTL("vm.stats.vm.v_active_count", active_pages) == -1) + R_ERROR(R_IGNORE); + if (GETSYSCTL("vm.stats.vm.v_inactive_count", inactive_pages) == -1) + R_ERROR(R_IGNORE); + if (GETSYSCTL("kern.ipc.shmmax", shmmax) == -1) + R_ERROR(R_IGNORE); + + info -> totalram = (u_long)PAGE_2_UNIT(total_pages); + info -> freeram = (u_long)PAGE_2_UNIT(free_pages); + info -> bufferram = (u_long)PAGE_2_UNIT(active_pages); + info -> sharedram = shmmax / UNIT_S; + + /*high mem (todo)*/ + info -> totalhigh = 0; /*Does this supose to refer to HMA or reserved ram?*/ + info -> freehigh = 0; + + return 0; +} + +//============================================================================== + +void Logger::outputDebugString (const String& text) +{ + std::cerr << text << std::endl; +} + +//============================================================================== +SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() +{ + return FreeBSD; +} + +String SystemStats::getOperatingSystemName() +{ + return "FreeBSD"; +} + +bool SystemStats::isOperatingSystem64Bit() + +{ + #if BEAST_64BIT + return true; + #else + //xxx not sure how to find this out?.. + return false; + #endif +} + +//============================================================================== +namespace BSDStatsHelpers +{ + String getCpuInfo (const char* const key) + { + StringArray lines; + File ("/proc/cpuinfo").readLines (lines); + + for (int i = lines.size(); --i >= 0;) // (NB - it's important that this runs in reverse order) + if (lines[i].startsWithIgnoreCase (key)) + return lines[i].fromFirstOccurrenceOf (":", false, false).trim(); + + return String::empty; + } +} + +String SystemStats::getCpuVendor() +{ + return BSDStatsHelpers::getCpuInfo ("vendor_id"); +} + +int SystemStats::getCpuSpeedInMegaherz() +{ + return roundToInt (BSDStatsHelpers::getCpuInfo ("cpu MHz").getFloatValue()); +} + +int SystemStats::getMemorySizeInMegabytes() +{ + struct sysinfo sysi; + + if (sysinfo (&sysi) == 0) + return (sysi.totalram * sysi.mem_unit / (1024 * 1024)); + + return 0; +} + +int SystemStats::getPageSize() +{ + return sysconf (_SC_PAGESIZE); +} + +//============================================================================== +String SystemStats::getLogonName() +{ + const char* user = getenv ("USER"); + + if (user == nullptr) + { + struct passwd* const pw = getpwuid (getuid()); + if (pw != nullptr) + user = pw->pw_name; + } + + return CharPointer_UTF8 (user); +} + +String SystemStats::getFullUserName() +{ + return getLogonName(); +} + +String SystemStats::getComputerName() +{ + char name [256] = { 0 }; + if (gethostname (name, sizeof (name) - 1) == 0) + return name; + + return String::empty; +} + +String getLocaleValue (nl_item key) +{ + const char* oldLocale = ::setlocale (LC_ALL, ""); + return String (const_cast (nl_langinfo (key))); + ::setlocale (LC_ALL, oldLocale); +} + +String SystemStats::getUserLanguage() +{ + return "Uknown user language"; +} + +String SystemStats::getUserRegion() +{ + return "Unknown user region"; +} + +String SystemStats::getDisplayLanguage() +{ + return getUserLanguage(); +} + +//============================================================================== +SystemStats::CPUFlags::CPUFlags() +{ + const String flags (BSDStatsHelpers::getCpuInfo ("flags")); + hasMMX = flags.contains ("mmx"); + hasSSE = flags.contains ("sse"); + hasSSE2 = flags.contains ("sse2"); + has3DNow = flags.contains ("3dnow"); + + numCpus = BSDStatsHelpers::getCpuInfo ("processor").getIntValue() + 1; +} + +//============================================================================== +uint32 beast_millisecondsSinceStartup() noexcept +{ + timespec t; + clock_gettime (CLOCK_MONOTONIC, &t); + + return t.tv_sec * 1000 + t.tv_nsec / 1000000; +} + +int64 Time::getHighResolutionTicks() noexcept +{ + timespec t; + clock_gettime (CLOCK_MONOTONIC, &t); + + return (t.tv_sec * (int64) 1000000) + (t.tv_nsec / 1000); +} + +int64 Time::getHighResolutionTicksPerSecond() noexcept +{ + return 1000000; // (microseconds) +} + +double Time::getMillisecondCounterHiRes() noexcept +{ + return getHighResolutionTicks() * 0.001; +} + +bool Time::setSystemTimeToThisTime() const +{ + timeval t; + t.tv_sec = millisSinceEpoch / 1000; + t.tv_usec = (millisSinceEpoch - t.tv_sec * 1000) * 1000; + + return settimeofday (&t, 0) == 0; +} diff --git a/Subtrees/beast/modules/beast_core/native/beast_bsd_Threads.cpp b/Subtrees/beast/modules/beast_core/native/beast_bsd_Threads.cpp new file mode 100644 index 0000000000..5ce0db5553 --- /dev/null +++ b/Subtrees/beast/modules/beast_core/native/beast_bsd_Threads.cpp @@ -0,0 +1,85 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Portions of this file are from JUCE. + Copyright (c) 2013 - Raw Material Software Ltd. + Please visit http://www.juce.com + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +/* + Note that a lot of methods that you'd expect to find in this file actually + live in beast_posix_SharedCode.h! +*/ + +//============================================================================== +void Process::setPriority (const ProcessPriority prior) +{ + const int policy = (prior <= NormalPriority) ? SCHED_OTHER : SCHED_RR; + const int minp = sched_get_priority_min (policy); + const int maxp = sched_get_priority_max (policy); + + struct sched_param param; + + switch (prior) + { + case LowPriority: + case NormalPriority: param.sched_priority = 0; break; + case HighPriority: param.sched_priority = minp + (maxp - minp) / 4; break; + case RealtimePriority: param.sched_priority = minp + (3 * (maxp - minp) / 4); break; + default: bassertfalse; break; + } + + pthread_setschedparam (pthread_self(), policy, ¶m); +} + +void Process::terminate() +{ + exit (0); +} + +BEAST_API bool BEAST_CALLTYPE beast_isRunningUnderDebugger() +{ + static char testResult = 0; + + if (testResult == 0) + { + testResult = (char) ptrace (PT_TRACE_ME, 0, 0, 0); + + if (testResult >= 0) + { + ptrace (PT_DETACH, 0, (caddr_t) 1, 0); + testResult = 1; + } + } + + return testResult < 0; +} + +BEAST_API bool BEAST_CALLTYPE Process::isRunningUnderDebugger() +{ + return beast_isRunningUnderDebugger(); +} + +static void swapUserAndEffectiveUser() +{ + (void) setreuid (geteuid(), getuid()); + (void) setregid (getegid(), getgid()); +} + +void Process::raisePrivilege() { if (geteuid() != 0 && getuid() == 0) swapUserAndEffectiveUser(); } +void Process::lowerPrivilege() { if (geteuid() == 0 && getuid() != 0) swapUserAndEffectiveUser(); } diff --git a/Subtrees/beast/modules/beast_core/system/beast_StandardHeader.h b/Subtrees/beast/modules/beast_core/system/beast_StandardHeader.h index ce4e4f7efe..1c71142752 100644 --- a/Subtrees/beast/modules/beast_core/system/beast_StandardHeader.h +++ b/Subtrees/beast/modules/beast_core/system/beast_StandardHeader.h @@ -27,7 +27,7 @@ //============================================================================== /** Current BEAST version number. - See also SystemStats::getBEASTVersion() for a string version. + See also SystemStats::getBeastVersion() for a string version. */ #define BEAST_MAJOR_VERSION 0 #define BEAST_MINOR_VERSION 0 @@ -39,7 +39,7 @@ Bits 8 to 16 = minor version. Bits 0 to 8 = point release. - See also SystemStats::getBEASTVersion() for a string version. + See also SystemStats::getBeastVersion() for a string version. */ #define BEAST_VERSION ((BEAST_MAJOR_VERSION << 16) + (BEAST_MINOR_VERSION << 8) + BEAST_BUILDNUMBER) diff --git a/Subtrees/beast/modules/beast_core/system/beast_SystemStats.cpp b/Subtrees/beast/modules/beast_core/system/beast_SystemStats.cpp index 07a8b43c8c..81d3b5e1c4 100644 --- a/Subtrees/beast/modules/beast_core/system/beast_SystemStats.cpp +++ b/Subtrees/beast/modules/beast_core/system/beast_SystemStats.cpp @@ -27,7 +27,7 @@ const SystemStats::CPUFlags& SystemStats::getCPUFlags() return cpuFlags; } -String SystemStats::getBEASTVersion() +String SystemStats::getBeastVersion() { // Some basic tests, to keep an eye on things and make sure these types work ok // on all platforms. Let me know if any of these assertions fail on your system! @@ -41,7 +41,7 @@ String SystemStats::getBEASTVersion() static_bassert (sizeof (int64) == 8); static_bassert (sizeof (uint64) == 8); - return "BEAST v" BEAST_STRINGIFY(BEAST_MAJOR_VERSION) + return "Beast v" BEAST_STRINGIFY(BEAST_MAJOR_VERSION) "." BEAST_STRINGIFY(BEAST_MINOR_VERSION) "." BEAST_STRINGIFY(BEAST_BUILDNUMBER); } @@ -55,7 +55,7 @@ String SystemStats::getBEASTVersion() { BeastVersionPrinter() { - DBG (SystemStats::getBEASTVersion()); + DBG (SystemStats::getBeastVersion()); } }; diff --git a/Subtrees/beast/modules/beast_core/system/beast_SystemStats.h b/Subtrees/beast/modules/beast_core/system/beast_SystemStats.h index 761f7c91bf..16f004ea40 100644 --- a/Subtrees/beast/modules/beast_core/system/beast_SystemStats.h +++ b/Subtrees/beast/modules/beast_core/system/beast_SystemStats.h @@ -26,7 +26,6 @@ #include "../text/beast_StringArray.h" - //============================================================================== /** Contains methods for finding out about the current hardware and OS configuration. @@ -38,7 +37,7 @@ public: /** Returns the current version of BEAST, See also the BEAST_VERSION, BEAST_MAJOR_VERSION and BEAST_MINOR_VERSION macros. */ - static String getBEASTVersion(); + static String getBeastVersion(); //============================================================================== /** The set of possible results of the getOperatingSystemType() method. */ @@ -46,16 +45,16 @@ public: { UnknownOS = 0, - Linux = 0x2000, - Android = 0x3000, - iOS = 0x8000, - MacOSX_10_4 = 0x1004, MacOSX_10_5 = 0x1005, MacOSX_10_6 = 0x1006, MacOSX_10_7 = 0x1007, MacOSX_10_8 = 0x1008, + Linux = 0x2000, + FreeBSD = 0x2001, + Android = 0x3000, + Win2000 = 0x4105, WinXP = 0x4106, WinVista = 0x4107, @@ -64,6 +63,8 @@ public: Windows = 0x4000, /**< To test whether any version of Windows is running, you can use the expression ((getOperatingSystemType() & Windows) != 0). */ + + iOS = 0x8000 }; /** Returns the type of operating system we're running on.