summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/backupclient/BackupClientFileAttributes.cpp23
-rw-r--r--lib/backupclient/BackupClientFileAttributes.h3
-rw-r--r--lib/backupclient/BackupClientRestore.cpp350
-rw-r--r--lib/backupclient/BackupClientRestore.h4
-rw-r--r--lib/backupclient/BackupDaemonConfigVerify.cpp4
-rw-r--r--lib/backupclient/BackupStoreDirectory.cpp5
-rw-r--r--lib/backupclient/BackupStoreFile.cpp27
-rw-r--r--lib/backupclient/BackupStoreFile.h9
-rw-r--r--lib/backupclient/BackupStoreFileDiff.cpp69
-rw-r--r--lib/backupclient/BackupStoreFileEncodeStream.cpp19
-rw-r--r--lib/backupclient/BackupStoreFileEncodeStream.h2
-rw-r--r--lib/backupclient/BackupStoreFilenameClear.cpp7
-rw-r--r--lib/backupstore/BackupStoreCheck.cpp92
-rw-r--r--lib/backupstore/BackupStoreCheck2.cpp24
-rw-r--r--lib/backupstore/BackupStoreConfigVerify.cpp8
-rw-r--r--lib/backupstore/BackupStoreInfo.cpp3
-rw-r--r--lib/common/BannerText.h2
-rw-r--r--lib/common/Box.h22
-rw-r--r--lib/common/BoxConfig-MSVC.h (renamed from lib/win32/config.h.win32)18
-rw-r--r--lib/common/BoxPlatform.h15
-rw-r--r--lib/common/BoxPortsAndFiles.h23
-rw-r--r--lib/common/BoxTime.cpp31
-rw-r--r--lib/common/BoxTime.h5
-rw-r--r--lib/common/BufferedStream.cpp207
-rw-r--r--lib/common/BufferedStream.h43
-rw-r--r--lib/common/CommonException.txt1
-rw-r--r--lib/common/Configuration.cpp13
-rw-r--r--lib/common/Configuration.h11
-rw-r--r--lib/common/ConversionString.cpp2
-rw-r--r--lib/common/DebugMemLeakFinder.cpp181
-rw-r--r--lib/common/EventWatchFilesystemObject.cpp5
-rw-r--r--lib/common/ExcludeList.cpp109
-rw-r--r--lib/common/ExcludeList.h9
-rw-r--r--lib/common/FileStream.cpp28
-rw-r--r--lib/common/Guards.h10
-rw-r--r--lib/common/InvisibleTempFileStream.cpp39
-rw-r--r--lib/common/InvisibleTempFileStream.h35
-rw-r--r--lib/common/Logging.cpp334
-rw-r--r--lib/common/Logging.h197
-rw-r--r--lib/common/MainHelper.h1
-rw-r--r--lib/common/MemLeakFinder.h17
-rw-r--r--lib/common/PartialReadStream.cpp10
-rw-r--r--lib/common/PartialReadStream.h4
-rw-r--r--lib/common/PathUtils.cpp34
-rw-r--r--lib/common/PathUtils.h26
-rw-r--r--lib/common/ReadGatherStream.cpp9
-rw-r--r--lib/common/ReadLoggingStream.cpp207
-rw-r--r--lib/common/ReadLoggingStream.h43
-rw-r--r--lib/common/Test.h385
-rw-r--r--lib/common/Timer.cpp386
-rw-r--r--lib/common/Timer.h87
-rw-r--r--lib/common/UnixUser.cpp11
-rw-r--r--lib/common/Utils.cpp11
-rw-r--r--lib/common/ZeroStream.cpp170
-rw-r--r--lib/common/ZeroStream.h39
-rw-r--r--lib/crypto/Random.cpp3
-rw-r--r--lib/intercept/intercept.cpp495
-rw-r--r--lib/intercept/intercept.h47
-rw-r--r--lib/raidfile/RaidFileController.cpp11
-rw-r--r--lib/raidfile/RaidFileController.h3
-rw-r--r--lib/raidfile/RaidFileException.txt2
-rw-r--r--lib/raidfile/RaidFileRead.cpp55
-rw-r--r--lib/raidfile/RaidFileWrite.cpp72
-rw-r--r--lib/server/ConnectionException.txt2
-rw-r--r--lib/server/Daemon.cpp313
-rw-r--r--lib/server/Daemon.h5
-rw-r--r--lib/server/LocalProcessStream.cpp80
-rw-r--r--lib/server/Protocol.cpp4
-rw-r--r--lib/server/SSLLib.cpp16
-rw-r--r--lib/server/ServerControl.h178
-rw-r--r--lib/server/ServerStream.h70
-rw-r--r--lib/server/Socket.cpp9
-rw-r--r--lib/server/SocketStream.cpp5
-rw-r--r--lib/server/SocketStream.h3
-rw-r--r--lib/server/SocketStreamTLS.cpp33
-rw-r--r--lib/server/TLSContext.cpp5
-rw-r--r--lib/server/WinNamedPipeStream.cpp367
-rw-r--r--lib/server/WinNamedPipeStream.h8
-rwxr-xr-xlib/server/makeprotocol.pl.in6
-rwxr-xr-xlib/win32/MSG00001.binbin0 -> 32 bytes
-rw-r--r--lib/win32/emu.cpp477
-rw-r--r--lib/win32/emu.h179
-rwxr-xr-xlib/win32/getopt_long.cxx547
83 files changed, 5521 insertions, 903 deletions
diff --git a/lib/backupclient/BackupClientFileAttributes.cpp b/lib/backupclient/BackupClientFileAttributes.cpp
index ba65d57a..925d1620 100644
--- a/lib/backupclient/BackupClientFileAttributes.cpp
+++ b/lib/backupclient/BackupClientFileAttributes.cpp
@@ -344,8 +344,8 @@ void BackupClientFileAttributes::ReadAttributes(const char *Filename, bool ZeroM
// to be true (still aborts), but it can at least hold 2^32.
if (winTime >= 0x100000000LL || _gmtime64(&winTime) == 0)
{
- ::syslog(LOG_ERR, "Invalid Modification Time "
- "caught for file: %s", Filename);
+ BOX_ERROR("Invalid Modification Time caught for "
+ "file: '" << Filename << "'");
pattr->ModificationTime = 0;
}
@@ -355,8 +355,8 @@ void BackupClientFileAttributes::ReadAttributes(const char *Filename, bool ZeroM
if (winTime > 0x100000000LL || _gmtime64(&winTime) == 0)
{
- ::syslog(LOG_ERR, "Invalid Attribute Modification "
- "Time caught for file: %s", Filename);
+ BOX_ERROR("Invalid Attribute Modification Time "
+ "caught for file: '" << Filename << "'");
pattr->AttrModificationTime = 0;
}
#endif
@@ -578,7 +578,8 @@ void BackupClientFileAttributes::FillExtendedAttr(StreamableMemBlock &outputBloc
// Created: 2003/10/07
//
// --------------------------------------------------------------------------
-void BackupClientFileAttributes::WriteAttributes(const char *Filename) const
+void BackupClientFileAttributes::WriteAttributes(const char *Filename,
+ bool MakeUserWritable) const
{
// Got something loaded
if(GetSize() <= 0)
@@ -626,9 +627,8 @@ void BackupClientFileAttributes::WriteAttributes(const char *Filename) const
}
#ifdef WIN32
- ::syslog(LOG_WARNING,
- "Cannot create symbolic links on Windows: %s",
- Filename);
+ BOX_WARNING("Cannot create symbolic links on Windows: '" <<
+ Filename << "'");
#else
// Make a symlink, first deleting anything in the way
::unlink(Filename);
@@ -704,7 +704,12 @@ void BackupClientFileAttributes::WriteAttributes(const char *Filename) const
THROW_EXCEPTION(CommonException, OSFileError)
}
}
-
+
+ if (MakeUserWritable)
+ {
+ mode |= S_IRWXU;
+ }
+
// Apply everything else... (allowable mode flags only)
if(::chmod(Filename, mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX)) != 0) // mode must be done last (think setuid)
{
diff --git a/lib/backupclient/BackupClientFileAttributes.h b/lib/backupclient/BackupClientFileAttributes.h
index 62ba2184..fa56ff65 100644
--- a/lib/backupclient/BackupClientFileAttributes.h
+++ b/lib/backupclient/BackupClientFileAttributes.h
@@ -45,7 +45,8 @@ public:
void ReadAttributes(const char *Filename, bool ZeroModificationTimes = false,
box_time_t *pModTime = 0, box_time_t *pAttrModTime = 0, int64_t *pFileSize = 0,
InodeRefType *pInodeNumber = 0, bool *pHasMultipleLinks = 0);
- void WriteAttributes(const char *Filename) const;
+ void WriteAttributes(const char *Filename,
+ bool MakeUserWritable = false) const;
bool IsSymLink() const;
diff --git a/lib/backupclient/BackupClientRestore.cpp b/lib/backupclient/BackupClientRestore.cpp
index fbb4ee47..3c03c87c 100644
--- a/lib/backupclient/BackupClientRestore.cpp
+++ b/lib/backupclient/BackupClientRestore.cpp
@@ -19,6 +19,7 @@
#include <set>
#include <limits.h>
#include <stdio.h>
+#include <errno.h>
#include "BackupClientRestore.h"
#include "autogen_BackupProtocolClient.h"
@@ -206,7 +207,7 @@ typedef struct
// Created: 23/11/03
//
// --------------------------------------------------------------------------
-static void BackupClientRestoreDir(BackupProtocolClient &rConnection, int64_t DirectoryID, std::string &rLocalDirectoryName,
+static int BackupClientRestoreDir(BackupProtocolClient &rConnection, int64_t DirectoryID, std::string &rLocalDirectoryName,
RestoreParams &Params, RestoreResumeInfo &rLevel)
{
// If we're resuming... check that we haven't got a next level to look at
@@ -223,11 +224,35 @@ static void BackupClientRestoreDir(BackupProtocolClient &rConnection, int64_t Di
rLevel.RemoveLevel();
}
- // Save the resumption information
- Params.mResumeInfo.Save(Params.mRestoreResumeInfoFilename);
+ // Create the local directory, if not already done.
+ // Path and owner set later, just use restrictive owner mode.
- // Create the local directory (if not already done) -- path and owner set later, just use restrictive owner mode
- switch(ObjectExists(rLocalDirectoryName.c_str()))
+ int exists;
+
+ try
+ {
+ exists = ObjectExists(rLocalDirectoryName.c_str());
+ }
+ catch (BoxException &e)
+ {
+ BOX_ERROR("Failed to check existence for " <<
+ rLocalDirectoryName << ": " << e.what());
+ return Restore_UnknownError;
+ }
+ catch(std::exception &e)
+ {
+ BOX_ERROR("Failed to check existence for " <<
+ rLocalDirectoryName << ": " << e.what());
+ return Restore_UnknownError;
+ }
+ catch(...)
+ {
+ BOX_ERROR("Failed to check existence for " <<
+ rLocalDirectoryName << ": unknown error");
+ return Restore_UnknownError;
+ }
+
+ switch(exists)
{
case ObjectExists_Dir:
// Do nothing
@@ -235,26 +260,152 @@ static void BackupClientRestoreDir(BackupProtocolClient &rConnection, int64_t Di
case ObjectExists_File:
{
// File exists with this name, which is fun. Get rid of it.
- ::printf("WARNING: File present with name '%s', removing out of the way of restored directory. Use specific restore with ID to restore this object.", rLocalDirectoryName.c_str());
+ BOX_WARNING("File present with name '" <<
+ rLocalDirectoryName << "', removing " <<
+ "out of the way of restored directory. "
+ "Use specific restore with ID to "
+ "restore this object.");
if(::unlink(rLocalDirectoryName.c_str()) != 0)
{
- THROW_EXCEPTION(CommonException, OSFileError);
+ BOX_ERROR("Failed to delete file " <<
+ rLocalDirectoryName << ": " <<
+ strerror(errno));
+ return Restore_UnknownError;
}
- TRACE1("In restore, directory name collision with file %s", rLocalDirectoryName.c_str());
+ BOX_TRACE("In restore, directory name "
+ "collision with file " <<
+ rLocalDirectoryName);
}
- // follow through to... (no break)
+ break;
case ObjectExists_NoObject:
- if(::mkdir(rLocalDirectoryName.c_str(), S_IRWXU) != 0)
- {
- THROW_EXCEPTION(CommonException, OSFileError);
- }
+ // we'll create it in a second, after checking
+ // whether the parent directory exists
break;
default:
ASSERT(false);
break;
}
-
- // Fetch the directory listing from the server -- getting a list of files which is approparite to the restore type
+
+ std::string parentDirectoryName(rLocalDirectoryName);
+ if(parentDirectoryName[parentDirectoryName.size() - 1] ==
+ DIRECTORY_SEPARATOR_ASCHAR)
+ {
+ parentDirectoryName.resize(parentDirectoryName.size() - 1);
+ }
+
+ size_t lastSlash = parentDirectoryName.rfind(DIRECTORY_SEPARATOR_ASCHAR);
+
+ if(lastSlash == std::string::npos)
+ {
+ // might be a forward slash separator,
+ // especially in the unit tests!
+ lastSlash = parentDirectoryName.rfind('/');
+ }
+
+ if(lastSlash != std::string::npos)
+ {
+ // the target directory is a deep path, remove the last
+ // directory name and check that the resulting parent
+ // exists, otherwise the restore should fail.
+ parentDirectoryName.resize(lastSlash);
+
+ #ifdef WIN32
+ // if the path is a drive letter, then we need to
+ // add a a backslash to query the root directory.
+ if (lastSlash == 2 && parentDirectoryName[1] == ':')
+ {
+ parentDirectoryName += '\\';
+ }
+ else if (lastSlash == 0)
+ {
+ parentDirectoryName += '\\';
+ }
+ #endif
+
+ int parentExists;
+
+ try
+ {
+ parentExists = ObjectExists(parentDirectoryName.c_str());
+ }
+ catch (BoxException &e)
+ {
+ BOX_ERROR("Failed to check existence for " <<
+ parentDirectoryName << ": " << e.what());
+ return Restore_UnknownError;
+ }
+ catch(std::exception &e)
+ {
+ BOX_ERROR("Failed to check existence for " <<
+ parentDirectoryName << ": " << e.what());
+ return Restore_UnknownError;
+ }
+ catch(...)
+ {
+ BOX_ERROR("Failed to check existence for " <<
+ parentDirectoryName << ": unknown error");
+ return Restore_UnknownError;
+ }
+
+ switch(parentExists)
+ {
+ case ObjectExists_Dir:
+ // this is fine, do nothing
+ break;
+
+ case ObjectExists_File:
+ BOX_ERROR("Failed to restore: '" <<
+ parentDirectoryName << "' "
+ "is a file, but should be a "
+ "directory.");
+ return Restore_TargetPathNotFound;
+
+ case ObjectExists_NoObject:
+ BOX_ERROR("Failed to restore: parent '" <<
+ parentDirectoryName << "' of target "
+ "directory does not exist.");
+ return Restore_TargetPathNotFound;
+
+ default:
+ BOX_ERROR("Failed to restore: unknown "
+ "result from ObjectExists('" <<
+ parentDirectoryName << "')");
+ return Restore_UnknownError;
+ }
+ }
+
+ if((exists == ObjectExists_NoObject ||
+ exists == ObjectExists_File) &&
+ ::mkdir(rLocalDirectoryName.c_str(), S_IRWXU) != 0)
+ {
+ BOX_ERROR("Failed to create directory '" <<
+ rLocalDirectoryName << "': " <<
+ strerror(errno));
+ return Restore_UnknownError;
+ }
+
+ // Save the restore info, in case it's needed later
+ try
+ {
+ Params.mResumeInfo.Save(Params.mRestoreResumeInfoFilename);
+ }
+ catch(std::exception &e)
+ {
+ BOX_ERROR("Failed to save resume info file '" <<
+ Params.mRestoreResumeInfoFilename << "': " <<
+ e.what());
+ return Restore_UnknownError;
+ }
+ catch(...)
+ {
+ BOX_ERROR("Failed to save resume info file '" <<
+ Params.mRestoreResumeInfoFilename <<
+ "': unknown error");
+ return Restore_UnknownError;
+ }
+
+ // Fetch the directory listing from the server -- getting a
+ // list of files which is appropriate to the restore type
rConnection.QueryListDirectory(
DirectoryID,
Params.RestoreDeleted?(BackupProtocolClientListDirectory::Flags_Deleted):(BackupProtocolClientListDirectory::Flags_INCLUDE_EVERYTHING),
@@ -269,7 +420,23 @@ static void BackupClientRestoreDir(BackupProtocolClient &rConnection, int64_t Di
// Apply attributes to the directory
const StreamableMemBlock &dirAttrBlock(dir.GetAttributes());
BackupClientFileAttributes dirAttr(dirAttrBlock);
- dirAttr.WriteAttributes(rLocalDirectoryName.c_str());
+
+ try
+ {
+ dirAttr.WriteAttributes(rLocalDirectoryName.c_str(), true);
+ }
+ catch(std::exception &e)
+ {
+ BOX_ERROR("Failed to restore attributes for '" <<
+ rLocalDirectoryName << "': " << e.what());
+ return Restore_UnknownError;
+ }
+ catch(...)
+ {
+ BOX_ERROR("Failed to restore attributes for '" <<
+ rLocalDirectoryName << "': unknown error");
+ return Restore_UnknownError;
+ }
int64_t bytesWrittenSinceLastRestoreInfoSave = 0;
@@ -287,7 +454,13 @@ static void BackupClientRestoreDir(BackupProtocolClient &rConnection, int64_t Di
std::string localFilename(rLocalDirectoryName + DIRECTORY_SEPARATOR_ASCHAR + nm.GetClearFilename());
// Unlink anything which already exists -- for resuming restores, we can't overwrite files already there.
- ::unlink(localFilename.c_str());
+ if(::unlink(localFilename.c_str()) == 0)
+ {
+ BOX_ERROR("Failed to delete file '" <<
+ localFilename << "': " <<
+ strerror(errno));
+ return Restore_UnknownError;
+ }
// Request it from the store
rConnection.QueryGetFile(DirectoryID, en->GetObjectID());
@@ -297,17 +470,34 @@ static void BackupClientRestoreDir(BackupProtocolClient &rConnection, int64_t Di
// Decode the file -- need to do different things depending on whether
// the directory entry has additional attributes
- if(en->HasAttributes())
+ try
{
- // Use these attributes
- const StreamableMemBlock &storeAttr(en->GetAttributes());
- BackupClientFileAttributes attr(storeAttr);
- BackupStoreFile::DecodeFile(*objectStream, localFilename.c_str(), rConnection.GetTimeout(), &attr);
+ if(en->HasAttributes())
+ {
+ // Use these attributes
+ const StreamableMemBlock &storeAttr(en->GetAttributes());
+ BackupClientFileAttributes attr(storeAttr);
+ BackupStoreFile::DecodeFile(*objectStream, localFilename.c_str(), rConnection.GetTimeout(), &attr);
+ }
+ else
+ {
+ // Use attributes stored in file
+ BackupStoreFile::DecodeFile(*objectStream, localFilename.c_str(), rConnection.GetTimeout());
+ }
}
- else
+ catch(std::exception &e)
{
- // Use attributes stored in file
- BackupStoreFile::DecodeFile(*objectStream, localFilename.c_str(), rConnection.GetTimeout());
+ BOX_ERROR("Failed to restore file '" <<
+ localFilename << "': " <<
+ e.what());
+ return Restore_UnknownError;
+ }
+ catch(...)
+ {
+ BOX_ERROR("Failed to restore file '" <<
+ localFilename <<
+ "': unknown error");
+ return Restore_UnknownError;
}
// Progress display?
@@ -322,7 +512,34 @@ static void BackupClientRestoreDir(BackupProtocolClient &rConnection, int64_t Di
// Save restore info?
int64_t fileSize;
- if(FileExists(localFilename.c_str(), &fileSize, true /* treat links as not existing */))
+ int exists;
+
+ try
+ {
+ exists = FileExists(
+ localFilename.c_str(),
+ &fileSize,
+ true /* treat links as not
+ existing */);
+ }
+ catch(std::exception &e)
+ {
+ BOX_ERROR("Failed to determine "
+ "whether file exists: '" <<
+ localFilename << "': " <<
+ e.what());
+ return Restore_UnknownError;
+ }
+ catch(...)
+ {
+ BOX_ERROR("Failed to determine "
+ "whether file exists: '" <<
+ localFilename << "': "
+ "unknown error");
+ return Restore_UnknownError;
+ }
+
+ if(exists)
{
// File exists...
bytesWrittenSinceLastRestoreInfoSave += fileSize;
@@ -330,7 +547,25 @@ static void BackupClientRestoreDir(BackupProtocolClient &rConnection, int64_t Di
if(bytesWrittenSinceLastRestoreInfoSave > MAX_BYTES_WRITTEN_BETWEEN_RESTORE_INFO_SAVES)
{
// Save the restore info, in case it's needed later
- Params.mResumeInfo.Save(Params.mRestoreResumeInfoFilename);
+ try
+ {
+ Params.mResumeInfo.Save(Params.mRestoreResumeInfoFilename);
+ }
+ catch(std::exception &e)
+ {
+ BOX_ERROR("Failed to save resume info file '" <<
+ Params.mRestoreResumeInfoFilename <<
+ "': " << e.what());
+ return Restore_UnknownError;
+ }
+ catch(...)
+ {
+ BOX_ERROR("Failed to save resume info file '" <<
+ Params.mRestoreResumeInfoFilename <<
+ "': unknown error");
+ return Restore_UnknownError;
+ }
+
bytesWrittenSinceLastRestoreInfoSave = 0;
}
}
@@ -342,12 +577,31 @@ static void BackupClientRestoreDir(BackupProtocolClient &rConnection, int64_t Di
if(bytesWrittenSinceLastRestoreInfoSave != 0)
{
// Save the restore info, in case it's needed later
- Params.mResumeInfo.Save(Params.mRestoreResumeInfoFilename);
+ try
+ {
+ Params.mResumeInfo.Save(
+ Params.mRestoreResumeInfoFilename);
+ }
+ catch(std::exception &e)
+ {
+ BOX_ERROR("Failed to save resume info file '" <<
+ Params.mRestoreResumeInfoFilename << "': " <<
+ e.what());
+ return Restore_UnknownError;
+ }
+ catch(...)
+ {
+ BOX_ERROR("Failed to save resume info file '" <<
+ Params.mRestoreResumeInfoFilename <<
+ "': unknown error");
+ return Restore_UnknownError;
+ }
+
bytesWrittenSinceLastRestoreInfoSave = 0;
}
- // Recuse to directories
+ // Recurse to directories
{
BackupStoreDirectory::Iterator i(dir);
BackupStoreDirectory::Entry *en = 0;
@@ -364,7 +618,14 @@ static void BackupClientRestoreDir(BackupProtocolClient &rConnection, int64_t Di
RestoreResumeInfo &rnextLevel(rLevel.AddLevel(en->GetObjectID(), nm.GetClearFilename()));
// Recurse
- BackupClientRestoreDir(rConnection, en->GetObjectID(), localDirname, Params, rnextLevel);
+ int result = BackupClientRestoreDir(
+ rConnection, en->GetObjectID(),
+ localDirname, Params, rnextLevel);
+
+ if (result != Restore_Complete)
+ {
+ return result;
+ }
// Remove the level for the above call
rLevel.RemoveLevel();
@@ -373,7 +634,27 @@ static void BackupClientRestoreDir(BackupProtocolClient &rConnection, int64_t Di
rLevel.mRestoredObjects.insert(en->GetObjectID());
}
}
- }
+ }
+
+ // now remove the user writable flag, if we added it earlier
+ try
+ {
+ dirAttr.WriteAttributes(rLocalDirectoryName.c_str(), false);
+ }
+ catch(std::exception &e)
+ {
+ BOX_ERROR("Failed to restore attributes for '" <<
+ rLocalDirectoryName << "': " << e.what());
+ return Restore_UnknownError;
+ }
+ catch(...)
+ {
+ BOX_ERROR("Failed to restore attributes for '" <<
+ rLocalDirectoryName << "': unknown error");
+ return Restore_UnknownError;
+ }
+
+ return Restore_Complete;
}
@@ -444,7 +725,12 @@ int BackupClientRestore(BackupProtocolClient &rConnection, int64_t DirectoryID,
// Restore the directory
std::string localName(LocalDirectoryName);
- BackupClientRestoreDir(rConnection, DirectoryID, localName, params, params.mResumeInfo);
+ int result = BackupClientRestoreDir(rConnection, DirectoryID,
+ localName, params, params.mResumeInfo);
+ if (result != Restore_Complete)
+ {
+ return result;
+ }
// Undelete the directory on the server?
if(RestoreDeleted && UndeleteAfterRestoreDeleted)
diff --git a/lib/backupclient/BackupClientRestore.h b/lib/backupclient/BackupClientRestore.h
index 4b382771..f7724030 100644
--- a/lib/backupclient/BackupClientRestore.h
+++ b/lib/backupclient/BackupClientRestore.h
@@ -16,7 +16,9 @@ enum
{
Restore_Complete = 0,
Restore_ResumePossible = 1,
- Restore_TargetExists = 2
+ Restore_TargetExists = 2,
+ Restore_TargetPathNotFound = 3,
+ Restore_UnknownError = 4,
};
int BackupClientRestore(BackupProtocolClient &rConnection, int64_t DirectoryID, const char *LocalDirectoryName,
diff --git a/lib/backupclient/BackupDaemonConfigVerify.cpp b/lib/backupclient/BackupDaemonConfigVerify.cpp
index 89ad4d54..9000ec6d 100644
--- a/lib/backupclient/BackupDaemonConfigVerify.cpp
+++ b/lib/backupclient/BackupDaemonConfigVerify.cpp
@@ -81,7 +81,9 @@ static const ConfigurationVerifyKey verifyrootkeys[] =
{"FileTrackingSizeThreshold", 0, ConfigTest_Exists | ConfigTest_IsInt, 0},
{"DiffingUploadSizeThreshold", 0, ConfigTest_Exists | ConfigTest_IsInt, 0},
{"StoreHostname", 0, ConfigTest_Exists, 0},
- {"ExtendedLogging", "no", ConfigTest_IsBool, 0}, // make value "yes" to enable in config file
+ {"ExtendedLogging", "no", ConfigTest_IsBool, 0}, // extended log to syslog
+ {"ExtendedLogFile", NULL, 0, 0}, // extended log to a file
+ {"LogAllFileAccess", "no", ConfigTest_IsBool, 0},
{"CommandSocket", 0, 0, 0}, // not compulsory to have this
{"KeepAliveTime", 0, ConfigTest_IsInt, 0}, // optional
diff --git a/lib/backupclient/BackupStoreDirectory.cpp b/lib/backupclient/BackupStoreDirectory.cpp
index 61e6461d..0d06da34 100644
--- a/lib/backupclient/BackupStoreDirectory.cpp
+++ b/lib/backupclient/BackupStoreDirectory.cpp
@@ -149,6 +149,11 @@ void BackupStoreDirectory::ReadFromStream(IOStream &rStream, int Timeout)
int count = ntohl(hdr.mNumEntries);
// Clear existing list
+ for(std::vector<Entry*>::iterator i = mEntries.begin();
+ i != mEntries.end(); i++)
+ {
+ delete (*i);
+ }
mEntries.clear();
// Read them in!
diff --git a/lib/backupclient/BackupStoreFile.cpp b/lib/backupclient/BackupStoreFile.cpp
index 278bf50a..7e93d59d 100644
--- a/lib/backupclient/BackupStoreFile.cpp
+++ b/lib/backupclient/BackupStoreFile.cpp
@@ -17,10 +17,8 @@
#include <string.h>
#include <new>
#include <string.h>
+
#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
- #ifndef WIN32
- #include <syslog.h>
- #endif
#include <stdio.h>
#endif
@@ -46,6 +44,7 @@
#include "ReadGatherStream.h"
#include "Random.h"
#include "BackupStoreFileEncodeStream.h"
+#include "Logging.h"
#include "MemLeakFindOn.h"
@@ -291,6 +290,20 @@ void BackupStoreFile::DecodeFile(IOStream &rEncodedFile, const char *DecodedFile
}
out.Close();
+
+ // The stream might have uncertain size, in which case
+ // we need to drain it to get the
+ // Protocol::ProtocolStreamHeader_EndOfStream byte
+ // out of our connection stream.
+ char buffer[1];
+ int drained = rEncodedFile.Read(buffer, 1);
+
+ // The Read will return 0 if we are actually at the end
+ // of the stream, but some tests decode files directly,
+ // in which case we are actually positioned at the start
+ // of the block index. I hope that reading an extra byte
+ // doesn't hurt!
+ // ASSERT(drained == 0);
// Write the attributes
stream->GetAttributes().WriteAttributes(DecodedFilename);
@@ -743,8 +756,7 @@ int BackupStoreFile::DecodedStream::Read(void *pBuffer, int NBytes, int Timeout)
// Warn and log this issue
if(!sWarnedAboutBackwardsCompatiblity)
{
- ::printf("WARNING: Decoded one or more files using backwards compatibility mode for block index.\n");
- ::syslog(LOG_ERR, "WARNING: Decoded one or more files using backwards compatibility mode for block index.\n");
+ BOX_WARNING("WARNING: Decoded one or more files using backwards compatibility mode for block index.");
sWarnedAboutBackwardsCompatiblity = true;
}
}
@@ -1485,9 +1497,8 @@ void BackupStoreFile::EncodingBuffer::Allocate(int Size)
// --------------------------------------------------------------------------
void BackupStoreFile::EncodingBuffer::Reallocate(int NewSize)
{
-#ifndef WIN32
- TRACE2("Reallocating EncodingBuffer from %d to %d\n", mBufferSize, NewSize);
-#endif
+ BOX_TRACE("Reallocating EncodingBuffer from " << mBufferSize <<
+ " to " << NewSize);
ASSERT(mpBuffer != 0);
uint8_t *buffer = (uint8_t*)BackupStoreFile::CodingChunkAlloc(NewSize);
if(buffer == 0)
diff --git a/lib/backupclient/BackupStoreFile.h b/lib/backupclient/BackupStoreFile.h
index 437b4232..3ee5ddb0 100644
--- a/lib/backupclient/BackupStoreFile.h
+++ b/lib/backupclient/BackupStoreFile.h
@@ -50,17 +50,16 @@ public:
DiffTimer();
virtual ~DiffTimer();
public:
- virtual void DoKeepAlive() = 0;
- virtual time_t GetTimeMgmtEpoch() = 0;
- virtual int GetMaximumDiffingTime() = 0;
- virtual int GetKeepaliveTime() = 0;
+ virtual void DoKeepAlive() = 0;
+ virtual int GetMaximumDiffingTime() = 0;
+ virtual bool IsManaged() = 0;
};
// --------------------------------------------------------------------------
//
// Class
// Name: BackupStoreFile
-// Purpose: Class to hold together utils for maniplating files.
+// Purpose: Class to hold together utils for manipulating files.
// Created: 2003/08/28
//
// --------------------------------------------------------------------------
diff --git a/lib/backupclient/BackupStoreFileDiff.cpp b/lib/backupclient/BackupStoreFileDiff.cpp
index ee09f1c8..3a3f1a5e 100644
--- a/lib/backupclient/BackupStoreFileDiff.cpp
+++ b/lib/backupclient/BackupStoreFileDiff.cpp
@@ -29,6 +29,7 @@
#include "RollingChecksum.h"
#include "MD5Digest.h"
#include "CommonException.h"
+#include "Timer.h"
#include "MemLeakFindOn.h"
@@ -52,31 +53,6 @@ static bool SecondStageMatch(BlocksAvailableEntry *pFirstInHashList, RollingChec
BlocksAvailableEntry *pIndex, std::map<int64_t, int64_t> &rFoundBlocks);
static void GenerateRecipe(BackupStoreFileEncodeStream::Recipe &rRecipe, BlocksAvailableEntry *pIndex, int64_t NumBlocks, std::map<int64_t, int64_t> &rFoundBlocks, int64_t SizeOfInputFile);
-// sDiffTimerExpired flags when the diff timer has expired. When true, the
-// diff routine should check the wall clock as soon as possible, to determine
-// whether it's time for a keepalive to be sent, or whether the diff has been
-// running for too long and should be terminated.
-static bool sDiffTimerExpired = false;
-
-
-// --------------------------------------------------------------------------
-//
-// Function
-// Name: BackupStoreFile::DiffTimerExpired()
-// Purpose: Notifies BackupStoreFile object that the diff operation
-// timer has expired, which may mean that a keepalive should
-// be sent, or the diff should be terminated. Called from an
-// external timer, so it should not do more than set a flag.
-//
-// Created: 19/1/06
-//
-// --------------------------------------------------------------------------
-void BackupStoreFile::DiffTimerExpired()
-{
- sDiffTimerExpired = true;
-}
-
-
// --------------------------------------------------------------------------
//
// Function
@@ -483,15 +459,11 @@ static void SearchForMatchingBlocks(IOStream &rFile, std::map<int64_t, int64_t>
BlocksAvailableEntry *pIndex, int64_t NumBlocks,
int32_t Sizes[BACKUP_FILE_DIFF_MAX_BLOCK_SIZES], DiffTimer *pDiffTimer)
{
- time_t TimeMgmtEpoch = 0;
- int MaximumDiffingTime = 0;
- int KeepAliveTime = 0;
+ Timer maximumDiffingTime(0);
- if (pDiffTimer)
+ if(pDiffTimer && pDiffTimer->IsManaged())
{
- TimeMgmtEpoch = pDiffTimer->GetTimeMgmtEpoch();
- MaximumDiffingTime = pDiffTimer->GetMaximumDiffingTime();
- KeepAliveTime = pDiffTimer->GetKeepaliveTime();
+ maximumDiffingTime = Timer(pDiffTimer->GetMaximumDiffingTime());
}
std::map<int64_t, int32_t> goodnessOfFit;
@@ -577,31 +549,20 @@ static void SearchForMatchingBlocks(IOStream &rFile, std::map<int64_t, int64_t>
int rollOverInitialBytes = 0;
while(true)
{
- if(sDiffTimerExpired)
+ if(maximumDiffingTime.HasExpired())
{
- ASSERT(TimeMgmtEpoch > 0);
ASSERT(pDiffTimer != NULL);
-
- time_t tTotalRunIntvl = time(NULL) - TimeMgmtEpoch;
-
- if(MaximumDiffingTime > 0 &&
- tTotalRunIntvl >= MaximumDiffingTime)
- {
- TRACE0("MaximumDiffingTime reached - "
- "suspending file diff\n");
- abortSearch = true;
- break;
- }
- else if(KeepAliveTime > 0)
- {
- TRACE0("KeepAliveTime reached - "
- "initiating keep-alive\n");
- pDiffTimer->DoKeepAlive();
- }
-
- sDiffTimerExpired = false;
+ TRACE0("MaximumDiffingTime reached - "
+ "suspending file diff\n");
+ abortSearch = true;
+ break;
}
-
+
+ if(pDiffTimer)
+ {
+ pDiffTimer->DoKeepAlive();
+ }
+
// Load in another block of data, and record how big it is
int bytesInEndings = rFile.Read(endings, Sizes[s]);
int tmp;
diff --git a/lib/backupclient/BackupStoreFileEncodeStream.cpp b/lib/backupclient/BackupStoreFileEncodeStream.cpp
index c692f18e..423c11a3 100644
--- a/lib/backupclient/BackupStoreFileEncodeStream.cpp
+++ b/lib/backupclient/BackupStoreFileEncodeStream.cpp
@@ -38,6 +38,7 @@ using namespace BackupStoreFileCryptVar;
BackupStoreFileEncodeStream::BackupStoreFileEncodeStream()
: mpRecipe(0),
mpFile(0),
+ mpLogging(0),
mStatus(Status_Header),
mSendData(true),
mTotalBlocks(0),
@@ -79,6 +80,13 @@ BackupStoreFileEncodeStream::~BackupStoreFileEncodeStream()
mpFile = 0;
}
+ // Clear up logging stream
+ if(mpLogging)
+ {
+ delete mpLogging;
+ mpLogging = 0;
+ }
+
// Free the recipe
if(mpRecipe != 0)
{
@@ -199,6 +207,9 @@ void BackupStoreFileEncodeStream::Setup(const char *Filename, BackupStoreFileEnc
{
// Open the file
mpFile = new FileStream(Filename);
+
+ // Create logging stream
+ mpLogging = new ReadLoggingStream(*mpFile);
// Work out the largest possible block required for the encoded data
mAllocatedBufferSize = BackupStoreFile::MaxBlockSizeForChunkSize(maxBlockClearSize);
@@ -267,7 +278,7 @@ void BackupStoreFileEncodeStream::CalculateBlockSizes(int64_t DataSize, int64_t
rNumBlocksOut = (DataSize + rBlockSizeOut - 1) / rBlockSizeOut;
- } while(rBlockSizeOut <= BACKUP_FILE_MAX_BLOCK_SIZE && rNumBlocksOut > BACKUP_FILE_INCREASE_BLOCK_SIZE_AFTER);
+ } while(rBlockSizeOut < BACKUP_FILE_MAX_BLOCK_SIZE && rNumBlocksOut > BACKUP_FILE_INCREASE_BLOCK_SIZE_AFTER);
// Last block size
rLastBlockSizeOut = DataSize - ((rNumBlocksOut - 1) * rBlockSizeOut);
@@ -474,7 +485,7 @@ void BackupStoreFileEncodeStream::SkipPreviousBlocksInInstruction()
}
// Move forward in the stream
- mpFile->Seek(sizeToSkip, IOStream::SeekType_Relative);
+ mpLogging->Seek(sizeToSkip, IOStream::SeekType_Relative);
}
@@ -518,14 +529,14 @@ void BackupStoreFileEncodeStream::EncodeCurrentBlock()
ASSERT(blockRawSize < mAllocatedBufferSize);
// Check file open
- if(mpFile == 0)
+ if(mpFile == 0 || mpLogging == 0)
{
// File should be open, but isn't. So logical error.
THROW_EXCEPTION(BackupStoreException, Internal)
}
// Read the data in
- if(!mpFile->ReadFullBuffer(mpRawBuffer, blockRawSize, 0 /* not interested in size if failure */))
+ if(!mpLogging->ReadFullBuffer(mpRawBuffer, blockRawSize, 0 /* not interested in size if failure */))
{
// TODO: Do something more intelligent, and abort this upload because the file
// has changed
diff --git a/lib/backupclient/BackupStoreFileEncodeStream.h b/lib/backupclient/BackupStoreFileEncodeStream.h
index 1c748798..fb5d0851 100644
--- a/lib/backupclient/BackupStoreFileEncodeStream.h
+++ b/lib/backupclient/BackupStoreFileEncodeStream.h
@@ -17,6 +17,7 @@
#include "CollectInBufferStream.h"
#include "MD5Digest.h"
#include "BackupStoreFile.h"
+#include "ReadLoggingStream.h"
namespace BackupStoreFileCreation
{
@@ -100,6 +101,7 @@ private:
Recipe *mpRecipe;
IOStream *mpFile; // source file
CollectInBufferStream mData; // buffer for header and index entries
+ ReadLoggingStream *mpLogging;
int mStatus;
bool mSendData; // true if there's file data to send (ie not a symlink)
int64_t mTotalBlocks; // Total number of blocks in the file
diff --git a/lib/backupclient/BackupStoreFilenameClear.cpp b/lib/backupclient/BackupStoreFilenameClear.cpp
index c415b9bb..9114fdd1 100644
--- a/lib/backupclient/BackupStoreFilenameClear.cpp
+++ b/lib/backupclient/BackupStoreFilenameClear.cpp
@@ -13,6 +13,7 @@
#include "CipherContext.h"
#include "CipherBlowfish.h"
#include "Guards.h"
+#include "Logging.h"
#include "MemLeakFindOn.h"
@@ -203,9 +204,9 @@ static void EnsureEncDecBufferSize(int BufSize)
{
if(sEncDecBufferSize < BufSize)
{
-#ifndef WIN32
- TRACE2("Reallocating filename encoding/decoding buffer from %d to %d\n", sEncDecBufferSize, BufSize);
-#endif
+ BOX_TRACE("Reallocating filename encoding/decoding "
+ "buffer from " << sEncDecBufferSize <<
+ " to " << BufSize);
spEncDecBuffer->Resize(BufSize);
sEncDecBufferSize = BufSize;
MEMLEAKFINDER_NOT_A_LEAK(*spEncDecBuffer);
diff --git a/lib/backupstore/BackupStoreCheck.cpp b/lib/backupstore/BackupStoreCheck.cpp
index 16eeecf9..176ece8f 100644
--- a/lib/backupstore/BackupStoreCheck.cpp
+++ b/lib/backupstore/BackupStoreCheck.cpp
@@ -102,7 +102,7 @@ void BackupStoreCheck::Check()
// Couldn't lock the account -- just stop now
if(!mQuiet)
{
- ::printf("Couldn't lock the account -- did not check.\nTry again later after the client has disconnected.\nAlternatively, forcibly kill the server.\n");
+ BOX_ERROR("Failed to lock the account -- did not check.\nTry again later after the client has disconnected.\nAlternatively, forcibly kill the server.");
}
THROW_EXCEPTION(BackupStoreException, CouldNotLockStoreAccount)
}
@@ -110,41 +110,43 @@ void BackupStoreCheck::Check()
if(!mQuiet && mFixErrors)
{
- ::printf("NOTE: Will fix errors encountered during checking.\n");
+ BOX_NOTICE("Will fix errors encountered during checking.");
}
// Phase 1, check objects
if(!mQuiet)
{
- ::printf("Check store account ID %08x\nPhase 1, check objects...\n", mAccountID);
+ BOX_INFO("Checking store account ID " <<
+ BOX_FORMAT_ACCOUNT(mAccountID) << "...");
+ BOX_INFO("Phase 1, check objects...");
}
CheckObjects();
// Phase 2, check directories
if(!mQuiet)
{
- ::printf("Phase 2, check directories...\n");
+ BOX_INFO("Phase 2, check directories...");
}
CheckDirectories();
// Phase 3, check root
if(!mQuiet)
{
- ::printf("Phase 3, check root...\n");
+ BOX_INFO("Phase 3, check root...");
}
CheckRoot();
// Phase 4, check unattached objects
if(!mQuiet)
{
- ::printf("Phase 4, fix unattached objects...\n");
+ BOX_INFO("Phase 4, fix unattached objects...");
}
CheckUnattachedObjects();
// Phase 5, fix bad info
if(!mQuiet)
{
- ::printf("Phase 5, fix unrecovered inconsistencies...\n");
+ BOX_INFO("Phase 5, fix unrecovered inconsistencies...");
}
FixDirsWithWrongContainerID();
FixDirsWithLostDirs();
@@ -152,7 +154,7 @@ void BackupStoreCheck::Check()
// Phase 6, regenerate store info
if(!mQuiet)
{
- ::printf("Phase 6, regenerate store info...\n");
+ BOX_INFO("Phase 6, regenerate store info...");
}
WriteNewStoreInfo();
@@ -160,29 +162,40 @@ void BackupStoreCheck::Check()
if(mNumberErrorsFound > 0)
{
- ::printf("%lld errors found\n", mNumberErrorsFound);
+ BOX_WARNING("Finished checking store account ID " <<
+ BOX_FORMAT_ACCOUNT(mAccountID) << ": " <<
+ mNumberErrorsFound << " errors found");
if(!mFixErrors)
{
- ::printf("NOTE: No changes to the store account have been made.\n");
+ BOX_WARNING("No changes to the store account "
+ "have been made.");
}
if(!mFixErrors && mNumberErrorsFound > 0)
{
- ::printf("Run again with fix option to fix these errors\n");
+ BOX_WARNING("Run again with fix option to "
+ "fix these errors");
}
- if(mNumberErrorsFound > 0)
+ if(mFixErrors && mNumberErrorsFound > 0)
{
- ::printf("You should now use bbackupquery on the client machine to examine the store.\n");
+ BOX_WARNING("You should now use bbackupquery "
+ "on the client machine to examine the store.");
if(mLostAndFoundDirectoryID != 0)
{
- ::printf("A lost+found directory was created in the account root.\n"\
- "This contains files and directories which could not be matched to existing directories.\n"\
- "bbackupd will delete this directory in a few days time.\n");
+ BOX_WARNING("A lost+found directory was "
+ "created in the account root.\n"
+ "This contains files and directories "
+ "which could not be matched to "
+ "existing directories.\n"\
+ "bbackupd will delete this directory "
+ "in a few days time.");
}
}
}
else
{
- ::printf("Store account checked, no errors found.\n");
+ BOX_NOTICE("Finished checking store account ID " <<
+ BOX_FORMAT_ACCOUNT(mAccountID) << ": "
+ "no errors found");
}
}
@@ -304,7 +317,10 @@ int64_t BackupStoreCheck::CheckObjectsScanDir(int64_t StartID, int Level, const
}
else
{
- ::printf("Spurious or invalid directory %s/%s found%s -- delete manually\n", rDirName.c_str(), (*i).c_str(), mFixErrors?", deleting":"");
+ BOX_WARNING("Spurious or invalid directory " <<
+ rDirName << DIRECTORY_SEPARATOR <<
+ (*i) << " found, " <<
+ (mFixErrors?"deleting":"delete manually"));
++mNumberErrorsFound;
}
}
@@ -336,7 +352,7 @@ void BackupStoreCheck::CheckObjectsDir(int64_t StartID)
// Check directory exists
if(!RaidFileRead::DirectoryExists(mDiscSetNumber, dirName))
{
- TRACE1("RaidFile dir %s does not exist\n", dirName.c_str());
+ BOX_WARNING("RaidFile dir " << dirName << " does not exist");
return;
}
@@ -378,9 +394,9 @@ void BackupStoreCheck::CheckObjectsDir(int64_t StartID)
if(!fileOK)
{
// Unexpected or bad file, delete it
- ::printf("Spurious file %s" DIRECTORY_SEPARATOR "%s "
- "found%s\n", dirName.c_str(), (*i).c_str(),
- mFixErrors?", deleting":"");
+ BOX_WARNING("Spurious file " << dirName <<
+ DIRECTORY_SEPARATOR << (*i) << " found" <<
+ (mFixErrors?", deleting":""));
++mNumberErrorsFound;
if(mFixErrors)
{
@@ -401,7 +417,9 @@ void BackupStoreCheck::CheckObjectsDir(int64_t StartID)
if(!CheckAndAddObject(StartID | i, dirName + leaf))
{
// File was bad, delete it
- ::printf("Corrupted file %s%s found%s\n", dirName.c_str(), leaf, mFixErrors?", deleting":"");
+ BOX_WARNING("Corrupted file " << dirName <<
+ leaf << " found" <<
+ (mFixErrors?", deleting":""));
++mNumberErrorsFound;
if(mFixErrors)
{
@@ -509,7 +527,7 @@ int64_t BackupStoreCheck::CheckFile(int64_t ObjectID, IOStream &rStream)
if(ObjectID == BACKUPSTORE_ROOT_DIRECTORY_ID)
{
// Get that dodgy thing deleted!
- ::printf("Have file as root directory. This is bad.\n");
+ BOX_ERROR("Have file as root directory. This is bad.");
return -1;
}
@@ -596,7 +614,9 @@ void BackupStoreCheck::CheckDirectories()
if(dir.CheckAndFix())
{
// Wasn't quite right, and has been modified
- ::printf("Directory ID %llx has bad structure\n", pblock->mID[e]);
+ BOX_WARNING("Directory ID " <<
+ BOX_FORMAT_OBJECTID(pblock->mID[e]) <<
+ " has bad structure");
++mNumberErrorsFound;
isModified = true;
}
@@ -622,7 +642,11 @@ void BackupStoreCheck::CheckDirectories()
!= ((en->GetFlags() & BackupStoreDirectory::Entry::Flags_Dir) == BackupStoreDirectory::Entry::Flags_Dir))
{
// Entry is of wrong type
- ::printf("Directory ID %llx references object %llx which has a different type than expected.\n", pblock->mID[e], en->GetObjectID());
+ BOX_WARNING("Directory ID " <<
+ BOX_FORMAT_OBJECTID(pblock->mID[e]) <<
+ " references object " <<
+ BOX_FORMAT_OBJECTID(en->GetObjectID()) <<
+ " which has a different type than expected.");
badEntry = true;
}
else
@@ -630,8 +654,12 @@ void BackupStoreCheck::CheckDirectories()
// Check that the entry is not already contained.
if(iflags & Flags_IsContained)
{
+ BOX_WARNING("Directory ID " <<
+ BOX_FORMAT_OBJECTID(pblock->mID[e]) <<
+ " references object " <<
+ BOX_FORMAT_OBJECTID(en->GetObjectID()) <<
+ " which is already contained.");
badEntry = true;
- ::printf("Directory ID %llx references object %llx which is already contained.\n", pblock->mID[e], en->GetObjectID());
}
else
{
@@ -645,13 +673,13 @@ void BackupStoreCheck::CheckDirectories()
if(iflags & Flags_IsDir)
{
// Add to will fix later list
- ::printf("Directory ID %llx has wrong container ID.\n", en->GetObjectID());
+ BOX_WARNING("Directory ID " << BOX_FORMAT_OBJECTID(en->GetObjectID()) << " has wrong container ID.");
mDirsWithWrongContainerID.push_back(en->GetObjectID());
}
else
{
// This is OK for files, they might move
- ::printf("File ID %llx has different container ID, probably moved\n", en->GetObjectID());
+ BOX_WARNING("File ID " << BOX_FORMAT_OBJECTID(en->GetObjectID()) << " has different container ID, probably moved");
}
// Fix entry for now
@@ -670,7 +698,7 @@ void BackupStoreCheck::CheckDirectories()
// Mark as changed
isModified = true;
// Tell user
- ::printf("Directory ID %llx has wrong size for object %llx\n", pblock->mID[e], en->GetObjectID());
+ BOX_WARNING("Directory ID " << BOX_FORMAT_OBJECTID(pblock->mID[e]) << " has wrong size for object " << BOX_FORMAT_OBJECTID(en->GetObjectID()));
}
}
}
@@ -686,7 +714,7 @@ void BackupStoreCheck::CheckDirectories()
{
// Just remove the entry
badEntry = true;
- ::printf("Directory ID %llx references object %llx which does not exist.\n", pblock->mID[e], en->GetObjectID());
+ BOX_WARNING("Directory ID " << BOX_FORMAT_OBJECTID(pblock->mID[e]) << " references object " << BOX_FORMAT_OBJECTID(en->GetObjectID()) << " which does not exist.");
}
}
@@ -729,7 +757,7 @@ void BackupStoreCheck::CheckDirectories()
if(isModified && mFixErrors)
{
- ::printf("Fixing directory ID %llx\n", pblock->mID[e]);
+ BOX_WARNING("Fixing directory ID " << BOX_FORMAT_OBJECTID(pblock->mID[e]));
// Save back to disc
RaidFileWrite fixed(mDiscSetNumber, filename);
diff --git a/lib/backupstore/BackupStoreCheck2.cpp b/lib/backupstore/BackupStoreCheck2.cpp
index 7bc9a109..9c6f2452 100644
--- a/lib/backupstore/BackupStoreCheck2.cpp
+++ b/lib/backupstore/BackupStoreCheck2.cpp
@@ -47,7 +47,7 @@ void BackupStoreCheck::CheckRoot()
}
else
{
- ::printf("Root directory doesn't exist\n");
+ BOX_WARNING("Root directory doesn't exist");
++mNumberErrorsFound;
@@ -118,7 +118,7 @@ void BackupStoreCheck::CheckUnattachedObjects()
if((flags & Flags_IsContained) == 0)
{
// Unattached object...
- ::printf("Object %llx is unattached.\n", pblock->mID[e]);
+ BOX_WARNING("Object " << BOX_FORMAT_OBJECTID(pblock->mID[e]) << " is unattached.");
++mNumberErrorsFound;
// What's to be done?
@@ -147,7 +147,7 @@ void BackupStoreCheck::CheckUnattachedObjects()
// Just delete it to be safe.
if(diffFromObjectID != 0)
{
- ::printf("Object %llx is unattached, and is a patch. Deleting, cannot reliably recover.\n", pblock->mID[e]);
+ BOX_WARNING("Object " << BOX_FORMAT_OBJECTID(pblock->mID[e]) << " is unattached, and is a patch. Deleting, cannot reliably recover.");
// Delete this object instead
if(mFixErrors)
@@ -229,11 +229,15 @@ bool BackupStoreCheck::TryToRecreateDirectory(int64_t MissingDirectoryID)
// Can recreate this! Wooo!
if(!mFixErrors)
{
- ::printf("Missing directory %llx could be recreated\n", MissingDirectoryID);
+ BOX_WARNING("Missing directory " <<
+ BOX_FORMAT_OBJECTID(MissingDirectoryID) <<
+ " could be recreated.");
mDirsAdded.insert(MissingDirectoryID);
return true;
}
- ::printf("Recreating missing directory %llx\n", MissingDirectoryID);
+
+ BOX_WARNING("Recreating missing directory " <<
+ BOX_FORMAT_OBJECTID(MissingDirectoryID));
// Create a blank directory
BackupStoreDirectory dir(MissingDirectoryID, missing->second /* containing dir ID */);
@@ -300,7 +304,7 @@ int64_t BackupStoreCheck::GetLostAndFoundDirID()
if(!dir.NameInUse(lostAndFound))
{
// Found a name which can be used
- ::printf("Lost and found dir has name %s\n", name);
+ BOX_WARNING("Lost and found dir has name " << name);
break;
}
}
@@ -524,7 +528,7 @@ void BackupStoreCheck::WriteNewStoreInfo()
}
catch(...)
{
- ::printf("Load of existing store info failed, regenerating.\n");
+ BOX_WARNING("Load of existing store info failed, regenerating.");
++mNumberErrorsFound;
}
@@ -551,7 +555,7 @@ void BackupStoreCheck::WriteNewStoreInfo()
}
else
{
- ::printf("NOTE: Soft limit for account changed to ensure housekeeping doesn't delete files on next run\n");
+ BOX_WARNING("Soft limit for account changed to ensure housekeeping doesn't delete files on next run.");
}
if(poldInfo.get() != 0 && poldInfo->GetBlocksHardLimit() > minHard)
{
@@ -559,7 +563,7 @@ void BackupStoreCheck::WriteNewStoreInfo()
}
else
{
- ::printf("NOTE: Hard limit for account changed to ensure housekeeping doesn't delete files on next run\n");
+ BOX_WARNING("Hard limit for account changed to ensure housekeeping doesn't delete files on next run.");
}
// Object ID
@@ -586,7 +590,7 @@ void BackupStoreCheck::WriteNewStoreInfo()
if(mFixErrors)
{
info->Save();
- ::printf("New store info file written successfully.\n");
+ BOX_NOTICE("New store info file written successfully.");
}
}
diff --git a/lib/backupstore/BackupStoreConfigVerify.cpp b/lib/backupstore/BackupStoreConfigVerify.cpp
index 6fa05d06..784adfb8 100644
--- a/lib/backupstore/BackupStoreConfigVerify.cpp
+++ b/lib/backupstore/BackupStoreConfigVerify.cpp
@@ -35,7 +35,13 @@ static const ConfigurationVerifyKey verifyrootkeys[] =
{"AccountDatabase", 0, ConfigTest_Exists, 0},
{"TimeBetweenHousekeeping", 0, ConfigTest_Exists | ConfigTest_IsInt, 0},
{"ExtendedLogging", "no", ConfigTest_IsBool, 0}, // make value "yes" to enable in config file
- {"RaidFileConf", BOX_FILE_RAIDFILE_DEFAULT_CONFIG, ConfigTest_LastEntry, 0}
+
+ #ifdef WIN32
+ {"RaidFileConf", "", ConfigTest_LastEntry, 0}
+ #else
+ {"RaidFileConf", BOX_FILE_RAIDFILE_DEFAULT_CONFIG, ConfigTest_LastEntry, 0}
+ #endif
+
};
const ConfigurationVerify BackupConfigFileVerify =
diff --git a/lib/backupstore/BackupStoreInfo.cpp b/lib/backupstore/BackupStoreInfo.cpp
index ce785dde..3588cc00 100644
--- a/lib/backupstore/BackupStoreInfo.cpp
+++ b/lib/backupstore/BackupStoreInfo.cpp
@@ -124,7 +124,8 @@ void BackupStoreInfo::CreateNew(int32_t AccountID, const std::string &rRootDir,
};
// Generate the filename
- ASSERT(rRootDir[rRootDir.size() - 1] == DIRECTORY_SEPARATOR_ASCHAR);
+ ASSERT(rRootDir[rRootDir.size() - 1] == '/' ||
+ rRootDir[rRootDir.size() - 1] == DIRECTORY_SEPARATOR_ASCHAR);
std::string fn(rRootDir + INFO_FILENAME);
// Open the file for writing
diff --git a/lib/common/BannerText.h b/lib/common/BannerText.h
index 7e28fa16..c9d85d5c 100644
--- a/lib/common/BannerText.h
+++ b/lib/common/BannerText.h
@@ -11,7 +11,7 @@
#define BANNERTEXT__H
#define BANNER_TEXT(UtilityName) \
- "Box " UtilityName " v" BOX_VERSION ", (c) Ben Summers and contributors 2003-2006\n"
+ "Box " UtilityName " v" BOX_VERSION ", (c) Ben Summers and contributors 2003-2007"
#endif // BANNERTEXT__H
diff --git a/lib/common/Box.h b/lib/common/Box.h
index eb33673c..abaad9f2 100644
--- a/lib/common/Box.h
+++ b/lib/common/Box.h
@@ -27,13 +27,14 @@
#endif
#ifdef SHOW_BACKTRACE_ON_EXCEPTION
- #include "Utils.h"
+ #include "Utils.h"
#define OPTIONAL_DO_BACKTRACE DumpStackBacktrace();
#else
#define OPTIONAL_DO_BACKTRACE
#endif
#include "CommonException.h"
+#include "Logging.h"
#ifndef NDEBUG
@@ -94,22 +95,27 @@
#ifdef BOX_MEMORY_LEAK_TESTING
// Memory leak testing
#include "MemLeakFinder.h"
+ #define DEBUG_NEW new(__FILE__,__LINE__)
#define MEMLEAKFINDER_NOT_A_LEAK(x) memleakfinder_notaleak(x);
+ #define MEMLEAKFINDER_NO_LEAKS MemLeakSuppressionGuard _guard;
+ #define MEMLEAKFINDER_INIT memleakfinder_init();
#define MEMLEAKFINDER_START {memleakfinder_global_enable = true;}
- #define MEMLEAKFINDER_STOP {memleakfinder_global_enable = false;}
+ #define MEMLEAKFINDER_STOP {memleakfinder_global_enable = false;}
#else
#define DEBUG_NEW new
#define MEMLEAKFINDER_NOT_A_LEAK(x)
+ #define MEMLEAKFINDER_NO_LEAKS
+ #define MEMLEAKFINDER_INIT
#define MEMLEAKFINDER_START
#define MEMLEAKFINDER_STOP
#endif
-
-#define THROW_EXCEPTION(type, subtype) \
- { \
- OPTIONAL_DO_BACKTRACE \
- TRACE1("Exception thrown: " #type "(" #subtype ") at " __FILE__ "(%d)\n", __LINE__) \
- throw type(type::subtype); \
+#define THROW_EXCEPTION(type, subtype) \
+ { \
+ OPTIONAL_DO_BACKTRACE \
+ BOX_WARNING("Exception thrown: " #type "(" #subtype ") at " \
+ __FILE__ "(" << __LINE__ << ")") \
+ throw type(type::subtype); \
}
// extra macros for converting to network byte order
diff --git a/lib/win32/config.h.win32 b/lib/common/BoxConfig-MSVC.h
index edc44a75..6ce496f5 100644
--- a/lib/win32/config.h.win32
+++ b/lib/common/BoxConfig-MSVC.h
@@ -56,6 +56,10 @@
you don't. */
#define HAVE_DECL_XATTR_NOFOLLOW 0
+/* Define to 1 if you have the declaration of `O_BINARY', and to 0 if you
+ don't. */
+#define HAVE_DECL_O_BINARY 1
+
/* Define to 1 if #define of pragmas works */
/* #undef HAVE_DEFINE_PRAGMA */
@@ -82,7 +86,7 @@
/* #undef HAVE_GETPEEREID */
/* Define to 1 if you have the `getpid' function. */
-#define HAVE_GETPID 1
+// #define HAVE_GETPID 1
/* Define to 1 if you have the `getxattr' function. */
/* #undef HAVE_GETXATTR */
@@ -172,7 +176,9 @@
/* #undef HAVE_READLINE_READLINE_H */
/* Define to 1 if you have the <regex.h> header file. */
-// #define HAVE_REGEX_H 1
+/* #undef HAVE_REGEX_H */
+#define HAVE_PCREPOSIX_H 1
+#define HAVE_REGEX_SUPPORT 1
/* Define to 1 if you have the `setproctitle' function. */
/* #undef HAVE_SETPROCTITLE */
@@ -268,7 +274,7 @@
// #define HAVE_SYS_TIME_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
-#define HAVE_SYS_TYPES_H 1
+// #define HAVE_SYS_TYPES_H 1
/* Define to 1 if you have the <sys/wait.h> header file. */
/* #undef HAVE_SYS_WAIT_H */
@@ -318,6 +324,12 @@
/* Define to 1 if __syscall is available but needs a definition */
/* #undef HAVE___SYSCALL_NEED_DEFN */
+/* max value of long long calculated by configure */
+/* #undef LLONG_MAX */
+
+/* min value of long long calculated by configure */
+/* #undef LLONG_MIN */
+
/* Define to 1 if `lstat' dereferences a symlink specified with a trailing
slash. */
/* #undef LSTAT_FOLLOWS_SLASHED_SYMLINK */
diff --git a/lib/common/BoxPlatform.h b/lib/common/BoxPlatform.h
index cb83f7a9..4b58d31d 100644
--- a/lib/common/BoxPlatform.h
+++ b/lib/common/BoxPlatform.h
@@ -21,7 +21,12 @@
#define PLATFORM_DEV_NULL "/dev/null"
+#ifdef _MSC_VER
+#include "BoxConfig-MSVC.h"
+#include "BoxVersion.h"
+#else
#include "BoxConfig.h"
+#endif
#ifdef WIN32
// need msvcrt version 6.1 or higher for _gmtime64()
@@ -52,6 +57,14 @@
#define PLATFORM_DISABLE_MEM_LEAK_TESTING
#endif
+// Darwin also has a weird idea of permissions and dates on symlinks:
+// perms are fixed at creation time by your umask, and dates can't be
+// changed. This breaks unit tests if we try to compare these things.
+// See: http://lists.apple.com/archives/darwin-kernel/2006/Dec/msg00057.html
+#ifdef __APPLE__
+ #define PLATFORM_DISABLE_SYMLINK_ATTRIB_COMPARE
+#endif
+
// Find out if credentials on UNIX sockets can be obtained
#ifndef HAVE_GETPEEREID
#if !HAVE_DECL_SO_PEERCRED
@@ -97,8 +110,6 @@
#define HAVE_U_INT16_T
#define HAVE_U_INT32_T
#define HAVE_U_INT64_T
-
- typedef int pid_t;
#endif // WIN32 && !__MINGW32__
// Define missing types
diff --git a/lib/common/BoxPortsAndFiles.h b/lib/common/BoxPortsAndFiles.h
index 562c6724..a6ca9f6d 100644
--- a/lib/common/BoxPortsAndFiles.h
+++ b/lib/common/BoxPortsAndFiles.h
@@ -14,20 +14,27 @@
// Backup store daemon
-#define BOX_PORT_BBSTORED (BOX_PORT_BASE+1)
-#define BOX_FILE_BBSTORED_DEFAULT_CONFIG "/etc/box/bbstored.conf"
+#define BOX_PORT_BBSTORED (BOX_PORT_BASE+1)
+
// directory within the RAIDFILE root for the backup store daemon
-#define BOX_RAIDFILE_ROOT_BBSTORED "backup"
+#define BOX_RAIDFILE_ROOT_BBSTORED "backup"
-// Backup client daemon
+// configuration file paths
#ifdef WIN32
-#define BOX_FILE_BBACKUPD_DEFAULT_CONFIG "C:\\Program Files\\Box Backup\\bbackupd.conf"
+ // no default config file path, use these macros to call
+ // GetDefaultConfigFilePath() instead.
+
+ #define BOX_GET_DEFAULT_BBACKUPD_CONFIG_FILE \
+ GetDefaultConfigFilePath("bbackupd.conf").c_str()
+ #define BOX_GET_DEFAULT_RAIDFILE_CONFIG_FILE \
+ GetDefaultConfigFilePath("raidfile.conf").c_str()
+ #define BOX_GET_DEFAULT_BBSTORED_CONFIG_FILE \
+ GetDefaultConfigFilePath("bbstored.conf").c_str()
#else
#define BOX_FILE_BBACKUPD_DEFAULT_CONFIG "/etc/box/bbackupd.conf"
-#endif
-
-// RaidFile conf location default
#define BOX_FILE_RAIDFILE_DEFAULT_CONFIG "/etc/box/raidfile.conf"
+#define BOX_FILE_BBSTORED_DEFAULT_CONFIG "/etc/box/bbstored.conf"
+#endif
// Default name of the named pipe
#define BOX_NAMED_PIPE_NAME L"\\\\.\\pipe\\boxbackup"
diff --git a/lib/common/BoxTime.cpp b/lib/common/BoxTime.cpp
index 960fc329..1ddcffd4 100644
--- a/lib/common/BoxTime.cpp
+++ b/lib/common/BoxTime.cpp
@@ -9,7 +9,16 @@
#include "Box.h"
-#include <time.h>
+#ifdef HAVE_SYS_TIME_H
+ #include <sys/time.h>
+#endif
+
+#ifdef HAVE_TIME_H
+ #include <time.h>
+#endif
+
+#include <errno.h>
+#include <string.h>
#include "BoxTime.h"
@@ -19,13 +28,27 @@
//
// Function
// Name: GetCurrentBoxTime()
-// Purpose: Returns the current time as a box time. (1 sec precision)
+// Purpose: Returns the current time as a box time.
+// (1 sec precision, or better if supported by system)
// Created: 2003/10/08
//
// --------------------------------------------------------------------------
box_time_t GetCurrentBoxTime()
{
+ #ifdef HAVE_GETTIMEOFDAY
+ struct timeval tv;
+ if (gettimeofday(&tv, NULL) != 0)
+ {
+ BOX_ERROR("Failed to gettimeofday(), dropping "
+ "precision: " << strerror(errno));
+ }
+ else
+ {
+ box_time_t timeNow = (tv.tv_sec * MICRO_SEC_IN_SEC_LL)
+ + tv.tv_usec;
+ return timeNow;
+ }
+ #endif
+
return SecondsToBoxTime(time(0));
}
-
-
diff --git a/lib/common/BoxTime.h b/lib/common/BoxTime.h
index 398e6b1c..e62a77ab 100644
--- a/lib/common/BoxTime.h
+++ b/lib/common/BoxTime.h
@@ -35,6 +35,9 @@ inline uint64_t BoxTimeToMilliSeconds(box_time_t Time)
{
return Time / MILLI_SEC_IN_NANO_SEC_LL;
}
+inline uint64_t BoxTimeToMicroSeconds(box_time_t Time)
+{
+ return Time;
+}
#endif // BOXTIME__H
-
diff --git a/lib/common/BufferedStream.cpp b/lib/common/BufferedStream.cpp
new file mode 100644
index 00000000..288e1ed1
--- /dev/null
+++ b/lib/common/BufferedStream.cpp
@@ -0,0 +1,207 @@
+// --------------------------------------------------------------------------
+//
+// File
+// Name: BufferedStream.cpp
+// Purpose: Buffering wrapper around IOStreams
+// Created: 2007/01/16
+//
+// --------------------------------------------------------------------------
+
+#include "Box.h"
+#include "BufferedStream.h"
+#include "CommonException.h"
+
+#include <string.h>
+
+#include "MemLeakFindOn.h"
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: BufferedStream::BufferedStream(const char *, int, int)
+// Purpose: Constructor, set up buffer
+// Created: 2007/01/16
+//
+// --------------------------------------------------------------------------
+BufferedStream::BufferedStream(IOStream& rSource)
+: mrSource(rSource), mBufferSize(0), mBufferPosition(0)
+{ }
+
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: BufferedStream::Read(void *, int)
+// Purpose: Reads bytes from the file
+// Created: 2007/01/16
+//
+// --------------------------------------------------------------------------
+int BufferedStream::Read(void *pBuffer, int NBytes, int Timeout)
+{
+ if (mBufferSize == mBufferPosition)
+ {
+ // buffer is empty, fill it.
+
+ int numBytesRead = mrSource.Read(mBuffer, sizeof(mBuffer),
+ Timeout);
+
+ if (numBytesRead < 0)
+ {
+ return numBytesRead;
+ }
+
+ mBufferSize = numBytesRead;
+ }
+
+ int sizeToReturn = mBufferSize - mBufferPosition;
+
+ if (sizeToReturn > NBytes)
+ {
+ sizeToReturn = NBytes;
+ }
+
+ memcpy(pBuffer, mBuffer + mBufferPosition, sizeToReturn);
+ mBufferPosition += sizeToReturn;
+
+ if (mBufferPosition == mBufferSize)
+ {
+ // clear out the buffer
+ mBufferSize = 0;
+ mBufferPosition = 0;
+ }
+
+ return sizeToReturn;
+}
+
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: BufferedStream::BytesLeftToRead()
+// Purpose: Returns number of bytes to read (may not be most efficient function ever)
+// Created: 2007/01/16
+//
+// --------------------------------------------------------------------------
+IOStream::pos_type BufferedStream::BytesLeftToRead()
+{
+ return mrSource.BytesLeftToRead() + mBufferSize - mBufferPosition;
+}
+
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: BufferedStream::Write(void *, int)
+// Purpose: Writes bytes to the underlying stream (not supported)
+// Created: 2003/07/31
+//
+// --------------------------------------------------------------------------
+void BufferedStream::Write(const void *pBuffer, int NBytes)
+{
+ THROW_EXCEPTION(CommonException, NotSupported);
+}
+
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: BufferedStream::GetPosition()
+// Purpose: Get position in stream
+// Created: 2003/08/21
+//
+// --------------------------------------------------------------------------
+IOStream::pos_type BufferedStream::GetPosition() const
+{
+ return mrSource.GetPosition() - mBufferSize + mBufferPosition;
+}
+
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: BufferedStream::Seek(pos_type, int)
+// Purpose: Seeks within file, as lseek, invalidate buffer
+// Created: 2003/07/31
+//
+// --------------------------------------------------------------------------
+void BufferedStream::Seek(IOStream::pos_type Offset, int SeekType)
+{
+ switch (SeekType)
+ {
+ case SeekType_Absolute:
+ {
+ // just go there
+ mrSource.Seek(Offset, SeekType);
+ }
+ break;
+
+ case SeekType_Relative:
+ {
+ // Actual underlying file position is
+ // (mBufferSize - mBufferPosition) ahead of us.
+ // Need to subtract that amount from the seek
+ // to seek forward that much less, putting the
+ // real pointer in the right place.
+ mrSource.Seek(Offset - mBufferSize + mBufferPosition,
+ SeekType);
+ }
+ break;
+
+ case SeekType_End:
+ {
+ // Actual underlying file position is
+ // (mBufferSize - mBufferPosition) ahead of us.
+ // Need to add that amount to the seek
+ // to seek backwards that much more, putting the
+ // real pointer in the right place.
+ mrSource.Seek(Offset + mBufferSize - mBufferPosition,
+ SeekType);
+ }
+ }
+
+ // always clear the buffer for now (may be slightly wasteful)
+ mBufferSize = 0;
+ mBufferPosition = 0;
+}
+
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: BufferedStream::Close()
+// Purpose: Closes the underlying stream (not needed)
+// Created: 2003/07/31
+//
+// --------------------------------------------------------------------------
+void BufferedStream::Close()
+{
+ THROW_EXCEPTION(CommonException, NotSupported);
+}
+
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: BufferedStream::StreamDataLeft()
+// Purpose: Any data left to write?
+// Created: 2003/08/02
+//
+// --------------------------------------------------------------------------
+bool BufferedStream::StreamDataLeft()
+{
+ return mrSource.StreamDataLeft();
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: BufferedStream::StreamClosed()
+// Purpose: Is the stream closed?
+// Created: 2003/08/02
+//
+// --------------------------------------------------------------------------
+bool BufferedStream::StreamClosed()
+{
+ return mrSource.StreamClosed();
+}
+
diff --git a/lib/common/BufferedStream.h b/lib/common/BufferedStream.h
new file mode 100644
index 00000000..234061c4
--- /dev/null
+++ b/lib/common/BufferedStream.h
@@ -0,0 +1,43 @@
+// --------------------------------------------------------------------------
+//
+// File
+// Name: BufferedStream.h
+// Purpose: Buffering wrapper around IOStreams
+// Created: 2007/01/16
+//
+// --------------------------------------------------------------------------
+
+#ifndef BUFFEREDSTREAM__H
+#define BUFFEREDSTREAM__H
+
+#include "IOStream.h"
+
+class BufferedStream : public IOStream
+{
+private:
+ IOStream& mrSource;
+ char mBuffer[4096];
+ int mBufferSize;
+ int mBufferPosition;
+
+public:
+ BufferedStream(IOStream& rSource);
+
+ virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite);
+ virtual pos_type BytesLeftToRead();
+ virtual void Write(const void *pBuffer, int NBytes);
+ virtual pos_type GetPosition() const;
+ virtual void Seek(IOStream::pos_type Offset, int SeekType);
+ virtual void Close();
+
+ virtual bool StreamDataLeft();
+ virtual bool StreamClosed();
+
+private:
+ BufferedStream(const BufferedStream &rToCopy)
+ : mrSource(rToCopy.mrSource) { /* do not call */ }
+};
+
+#endif // BUFFEREDSTREAM__H
+
+
diff --git a/lib/common/CommonException.txt b/lib/common/CommonException.txt
index 5fa443d0..b2819886 100644
--- a/lib/common/CommonException.txt
+++ b/lib/common/CommonException.txt
@@ -44,3 +44,4 @@ KQueueNotSupportedOnThisPlatform 36
IOStreamGetLineNotEnoughDataToIgnore 37 Bad value passed to IOStreamGetLine::IgnoreBufferedData()
TempDirPathTooLong 38 Your temporary directory path is too long. Check the TMP and TEMP environment variables.
ArchiveBlockIncompleteRead 39 The Store Object Info File is too short or corrupted, and will be rewritten automatically when the next backup completes.
+AccessDenied 40 Access to the file or directory was denied. Please check the permissions.
diff --git a/lib/common/Configuration.cpp b/lib/common/Configuration.cpp
index def93571..5f9376c5 100644
--- a/lib/common/Configuration.cpp
+++ b/lib/common/Configuration.cpp
@@ -83,19 +83,16 @@ Configuration::~Configuration()
// Created: 2003/07/23
//
// --------------------------------------------------------------------------
-std::auto_ptr<Configuration> Configuration::LoadAndVerify(const char *Filename, const ConfigurationVerify *pVerify, std::string &rErrorMsg)
+std::auto_ptr<Configuration> Configuration::LoadAndVerify(
+ const std::string& rFilename,
+ const ConfigurationVerify *pVerify,
+ std::string &rErrorMsg)
{
- // Check arguments
- if(Filename == 0)
- {
- THROW_EXCEPTION(CommonException, BadArguments)
- }
-
// Just to make sure
rErrorMsg.erase();
// Open the file
- FileHandleGuard<O_RDONLY> file(Filename);
+ FileHandleGuard<O_RDONLY> file(rFilename);
// GetLine object
FdGetLine getline(file);
diff --git a/lib/common/Configuration.h b/lib/common/Configuration.h
index 4c455b0f..64e7568e 100644
--- a/lib/common/Configuration.h
+++ b/lib/common/Configuration.h
@@ -69,8 +69,15 @@ public:
MultiValueSeparator = '\x01'
};
- static std::auto_ptr<Configuration> LoadAndVerify(const char *Filename, const ConfigurationVerify *pVerify, std::string &rErrorMsg);
- static std::auto_ptr<Configuration> Load(const char *Filename, std::string &rErrorMsg) { return LoadAndVerify(Filename, 0, rErrorMsg); }
+ static std::auto_ptr<Configuration> LoadAndVerify(
+ const std::string& rFilename,
+ const ConfigurationVerify *pVerify,
+ std::string &rErrorMsg);
+
+ static std::auto_ptr<Configuration> Load(
+ const std::string& rFilename,
+ std::string &rErrorMsg)
+ { return LoadAndVerify(rFilename, 0, rErrorMsg); }
bool KeyExists(const char *pKeyName) const;
const std::string &GetKeyValue(const char *pKeyName) const;
diff --git a/lib/common/ConversionString.cpp b/lib/common/ConversionString.cpp
index b86bad4f..2d0a8d58 100644
--- a/lib/common/ConversionString.cpp
+++ b/lib/common/ConversionString.cpp
@@ -123,7 +123,7 @@ int32_t BoxConvert::_ConvertStringToInt(const char *pString, int Size)
void BoxConvert::_ConvertIntToString(std::string &rTo, int32_t From)
{
char text[64]; // size more than enough
- ::sprintf(text, "%d", From);
+ ::sprintf(text, "%d", (int)From);
rTo = text;
}
diff --git a/lib/common/DebugMemLeakFinder.cpp b/lib/common/DebugMemLeakFinder.cpp
index 17a20a6d..e9b0e681 100644
--- a/lib/common/DebugMemLeakFinder.cpp
+++ b/lib/common/DebugMemLeakFinder.cpp
@@ -25,6 +25,9 @@
#include <string.h>
#include <set>
+#include "MemLeakFinder.h"
+
+static bool memleakfinder_initialised = false;
bool memleakfinder_global_enable = false;
typedef struct
@@ -46,6 +49,17 @@ namespace
{
static std::map<void *, MallocBlockInfo> sMallocBlocks;
static std::map<void *, ObjectInfo> sObjectBlocks;
+ static bool sTrackingDataDestroyed = false;
+
+ static class DestructionWatchdog
+ {
+ public:
+ ~DestructionWatchdog()
+ {
+ sTrackingDataDestroyed = true;
+ }
+ }
+ sWatchdog;
static bool sTrackMallocInSection = false;
static std::set<void *> sSectionMallocBlocks;
@@ -55,11 +69,41 @@ namespace
static std::set<void *> sNotLeaks;
void *sNotLeaksPre[1024];
- int sNotLeaksPreNum = 0;
+ size_t sNotLeaksPreNum = 0;
+}
+
+void memleakfinder_init()
+{
+ ASSERT(!memleakfinder_initialised);
+ memleakfinder_initialised = true;
}
+MemLeakSuppressionGuard::MemLeakSuppressionGuard()
+{
+ ASSERT(memleakfinder_global_enable);
+ memleakfinder_global_enable = false;
+}
+
+MemLeakSuppressionGuard::~MemLeakSuppressionGuard()
+{
+ ASSERT(!memleakfinder_global_enable);
+ memleakfinder_global_enable = true;
+}
+
+// these functions may well allocate memory, which we don't want to track.
+static int sInternalAllocDepth = 0;
+
+class InternalAllocGuard
+{
+ public:
+ InternalAllocGuard () { sInternalAllocDepth++; }
+ ~InternalAllocGuard() { sInternalAllocDepth--; }
+};
+
void memleakfinder_malloc_add_block(void *b, size_t size, const char *file, int line)
{
+ InternalAllocGuard guard;
+
if(b != 0)
{
MallocBlockInfo i;
@@ -75,11 +119,13 @@ void memleakfinder_malloc_add_block(void *b, size_t size, const char *file, int
}
}
-
void *memleakfinder_malloc(size_t size, const char *file, int line)
{
+ InternalAllocGuard guard;
+
void *b = ::malloc(size);
if(!memleakfinder_global_enable) return b;
+ if(!memleakfinder_initialised) return b;
memleakfinder_malloc_add_block(b, size, file, line);
@@ -89,7 +135,9 @@ void *memleakfinder_malloc(size_t size, const char *file, int line)
void *memleakfinder_realloc(void *ptr, size_t size)
{
- if(!memleakfinder_global_enable)
+ InternalAllocGuard guard;
+
+ if(!memleakfinder_global_enable || !memleakfinder_initialised)
{
return ::realloc(ptr, size);
}
@@ -133,7 +181,9 @@ void *memleakfinder_realloc(void *ptr, size_t size)
void memleakfinder_free(void *ptr)
{
- if(memleakfinder_global_enable)
+ InternalAllocGuard guard;
+
+ if(memleakfinder_global_enable && memleakfinder_initialised)
{
// Check it's been allocated
std::map<void *, MallocBlockInfo>::iterator i(sMallocBlocks.find(ptr));
@@ -143,7 +193,7 @@ void memleakfinder_free(void *ptr)
}
else
{
- TRACE1("Block %x freed, but not known. Error? Or allocated in startup static allocation?\n", ptr);
+ TRACE1("Block %p freed, but not known. Error? Or allocated in startup static allocation?\n", ptr);
}
if(sTrackMallocInSection)
@@ -160,30 +210,44 @@ void memleakfinder_free(void *ptr)
void memleakfinder_notaleak_insert_pre()
{
+ InternalAllocGuard guard;
+
if(!memleakfinder_global_enable) return;
- for(int l = 0; l < sNotLeaksPreNum; l++)
+ if(!memleakfinder_initialised) return;
+
+ for(size_t l = 0; l < sNotLeaksPreNum; l++)
{
sNotLeaks.insert(sNotLeaksPre[l]);
}
+
sNotLeaksPreNum = 0;
}
bool is_leak(void *ptr)
{
+ InternalAllocGuard guard;
+
+ ASSERT(memleakfinder_initialised);
memleakfinder_notaleak_insert_pre();
return sNotLeaks.find(ptr) == sNotLeaks.end();
}
void memleakfinder_notaleak(void *ptr)
{
+ InternalAllocGuard guard;
+
+ ASSERT(!sTrackingDataDestroyed);
+
memleakfinder_notaleak_insert_pre();
- if(memleakfinder_global_enable)
+ if(memleakfinder_global_enable && memleakfinder_initialised)
{
sNotLeaks.insert(ptr);
}
else
{
- sNotLeaksPre[sNotLeaksPreNum++] = ptr;
+ if ( sNotLeaksPreNum <
+ sizeof(sNotLeaksPre)/sizeof(*sNotLeaksPre) )
+ sNotLeaksPre[sNotLeaksPreNum++] = ptr;
}
/* {
std::map<void *, MallocBlockInfo>::iterator i(sMallocBlocks.find(ptr));
@@ -204,6 +268,11 @@ void memleakfinder_notaleak(void *ptr)
// start monitoring a section of code
void memleakfinder_startsectionmonitor()
{
+ InternalAllocGuard guard;
+
+ ASSERT(memleakfinder_initialised);
+ ASSERT(!sTrackingDataDestroyed);
+
sTrackMallocInSection = true;
sSectionMallocBlocks.clear();
sTrackObjectsInSection = true;
@@ -213,6 +282,11 @@ void memleakfinder_startsectionmonitor()
// trace all blocks allocated and still allocated since memleakfinder_startsectionmonitor() called
void memleakfinder_traceblocksinsection()
{
+ InternalAllocGuard guard;
+
+ ASSERT(memleakfinder_initialised);
+ ASSERT(!sTrackingDataDestroyed);
+
std::set<void *>::iterator s(sSectionMallocBlocks.begin());
for(; s != sSectionMallocBlocks.end(); ++s)
{
@@ -223,17 +297,22 @@ void memleakfinder_traceblocksinsection()
}
else
{
- TRACE4("Block 0x%08p size %d allocated at %s:%d\n", i->first, i->second.size, i->second.file, i->second.line);
+ TRACE4("Block %p size %d allocated at %s:%d\n", i->first, i->second.size, i->second.file, i->second.line);
}
}
for(std::map<void *, ObjectInfo>::const_iterator i(sSectionObjectBlocks.begin()); i != sSectionObjectBlocks.end(); ++i)
{
- TRACE5("Object%s 0x%08p size %d allocated at %s:%d\n", i->second.array?" []":"", i->first, i->second.size, i->second.file, i->second.line);
+ TRACE5("Object%s %p size %d allocated at %s:%d\n", i->second.array?" []":"", i->first, i->second.size, i->second.file, i->second.line);
}
}
int memleakfinder_numleaks()
{
+ InternalAllocGuard guard;
+
+ ASSERT(memleakfinder_initialised);
+ ASSERT(!sTrackingDataDestroyed);
+
int n = 0;
for(std::map<void *, MallocBlockInfo>::const_iterator i(sMallocBlocks.begin()); i != sMallocBlocks.end(); ++i)
@@ -243,6 +322,7 @@ int memleakfinder_numleaks()
for(std::map<void *, ObjectInfo>::const_iterator i(sObjectBlocks.begin()); i != sObjectBlocks.end(); ++i)
{
+ const ObjectInfo& rInfo = i->second;
if(is_leak(i->first)) ++n;
}
@@ -251,24 +331,32 @@ int memleakfinder_numleaks()
void memleakfinder_reportleaks_file(FILE *file)
{
+ InternalAllocGuard guard;
+
+ ASSERT(!sTrackingDataDestroyed);
+
for(std::map<void *, MallocBlockInfo>::const_iterator i(sMallocBlocks.begin()); i != sMallocBlocks.end(); ++i)
{
- if(is_leak(i->first)) ::fprintf(file, "Block 0x%08p size %d allocated at %s:%d\n", i->first, i->second.size, i->second.file, i->second.line);
+ if(is_leak(i->first)) ::fprintf(file, "Block 0x%p size %d allocated at %s:%d\n", i->first, i->second.size, i->second.file, i->second.line);
}
for(std::map<void *, ObjectInfo>::const_iterator i(sObjectBlocks.begin()); i != sObjectBlocks.end(); ++i)
{
- if(is_leak(i->first)) ::fprintf(file, "Object%s 0x%08p size %d allocated at %s:%d\n", i->second.array?" []":"", i->first, i->second.size, i->second.file, i->second.line);
+ if(is_leak(i->first)) ::fprintf(file, "Object%s 0x%p size %d allocated at %s:%d\n", i->second.array?" []":"", i->first, i->second.size, i->second.file, i->second.line);
}
}
void memleakfinder_reportleaks()
{
+ InternalAllocGuard guard;
+
// report to stdout
memleakfinder_reportleaks_file(stdout);
}
void memleakfinder_reportleaks_appendfile(const char *filename, const char *markertext)
{
+ InternalAllocGuard guard;
+
FILE *file = ::fopen(filename, "a");
if(file != 0)
{
@@ -286,7 +374,8 @@ void memleakfinder_reportleaks_appendfile(const char *filename, const char *mark
}
else
{
- printf("WARNING: Couldn't open memory leak results file %s for appending\n", filename);
+ BOX_WARNING("Couldn't open memory leak results file " <<
+ filename << " for appending");
}
}
@@ -315,7 +404,11 @@ void memleakfinder_setup_exit_report(const char *filename, const char *markertex
void add_object_block(void *block, size_t size, const char *file, int line, bool array)
{
+ InternalAllocGuard guard;
+
if(!memleakfinder_global_enable) return;
+ if(!memleakfinder_initialised) return;
+ ASSERT(!sTrackingDataDestroyed);
if(block != 0)
{
@@ -335,7 +428,11 @@ void add_object_block(void *block, size_t size, const char *file, int line, bool
void remove_object_block(void *block)
{
+ InternalAllocGuard guard;
+
if(!memleakfinder_global_enable) return;
+ if(!memleakfinder_initialised) return;
+ if(sTrackingDataDestroyed) return;
std::map<void *, ObjectInfo>::iterator i(sObjectBlocks.find(block));
if(i != sObjectBlocks.end())
@@ -355,34 +452,68 @@ void remove_object_block(void *block)
// If it's not in the list, just ignore it, as lots of stuff goes this way...
}
-void *operator new(size_t size, const char *file, int line)
+static void *internal_new(size_t size, const char *file, int line)
{
- void *r = ::malloc(size);
- add_object_block(r, size, file, line, false);
- //TRACE4("new(), %d, %s, %d, %08x\n", size, file, line, r);
+ void *r;
+
+ {
+ InternalAllocGuard guard;
+ r = ::malloc(size);
+ }
+
+ if (sInternalAllocDepth == 0)
+ {
+ InternalAllocGuard guard;
+ add_object_block(r, size, file, line, false);
+ //TRACE4("new(), %d, %s, %d, %08x\n", size, file, line, r);
+ }
+
return r;
}
+void *operator new(size_t size, const char *file, int line)
+{
+ return internal_new(size, file, line);
+}
+
void *operator new[](size_t size, const char *file, int line)
{
- void *r = ::malloc(size);
- add_object_block(r, size, file, line, true);
- //TRACE4("new[](), %d, %s, %d, %08x\n", size, file, line, r);
- return r;
+ return internal_new(size, file, line);
}
-void operator delete[](void *ptr) throw ()
+// where there is no doctor... need to override standard new() too
+// http://www.relisoft.com/book/tech/9new.html
+// disabled because it causes hangs on FC2 in futex() in test/common
+// while reading files. reason unknown.
+/*
+void *operator new(size_t size)
{
+ return internal_new(size, "standard libraries", 0);
+}
+*/
+
+void *operator new[](size_t size)
+{
+ return internal_new(size, "standard libraries", 0);
+}
+
+void internal_delete(void *ptr)
+{
+ InternalAllocGuard guard;
+
::free(ptr);
remove_object_block(ptr);
//TRACE1("delete[]() called, %08x\n", ptr);
}
+void operator delete[](void *ptr) throw ()
+{
+ internal_delete(ptr);
+}
+
void operator delete(void *ptr) throw ()
{
- ::free(ptr);
- remove_object_block(ptr);
- //TRACE1("delete() called, %08x\n", ptr);
+ internal_delete(ptr);
}
#endif // NDEBUG
diff --git a/lib/common/EventWatchFilesystemObject.cpp b/lib/common/EventWatchFilesystemObject.cpp
index a9508c22..84781113 100644
--- a/lib/common/EventWatchFilesystemObject.cpp
+++ b/lib/common/EventWatchFilesystemObject.cpp
@@ -9,6 +9,7 @@
#include "Box.h"
+#include <errno.h>
#include <fcntl.h>
#ifdef HAVE_UNISTD_H
@@ -17,6 +18,7 @@
#include "EventWatchFilesystemObject.h"
#include "autogen_CommonException.h"
+#include "Logging.h"
#include "MemLeakFindOn.h"
@@ -37,6 +39,9 @@ EventWatchFilesystemObject::EventWatchFilesystemObject(const char *Filename)
#ifdef HAVE_KQUEUE
if(mDescriptor == -1)
{
+ BOX_ERROR("EventWatchFilesystemObject: "
+ "Failed to open file '" << Filename << "': " <<
+ strerror(errno));
THROW_EXCEPTION(CommonException, OSFileOpenError)
}
#else
diff --git a/lib/common/ExcludeList.cpp b/lib/common/ExcludeList.cpp
index 43991594..b9f41634 100644
--- a/lib/common/ExcludeList.cpp
+++ b/lib/common/ExcludeList.cpp
@@ -9,8 +9,12 @@
#include "Box.h"
-#ifdef HAVE_REGEX_H
- #include <regex.h>
+#ifdef HAVE_REGEX_SUPPORT
+ #ifdef HAVE_PCREPOSIX_H
+ #include <pcreposix.h>
+ #else
+ #include <regex.h>
+ #endif
#define EXCLUDELIST_IMPLEMENTATION_REGEX_T_DEFINED
#endif
@@ -18,6 +22,7 @@
#include "Utils.h"
#include "Configuration.h"
#include "Archive.h"
+#include "Logging.h"
#include "MemLeakFindOn.h"
@@ -45,7 +50,7 @@ ExcludeList::ExcludeList()
// --------------------------------------------------------------------------
ExcludeList::~ExcludeList()
{
-#ifdef HAVE_REGEX_H
+#ifdef HAVE_REGEX_SUPPORT
// free regex memory
while(mRegex.size() > 0)
{
@@ -65,6 +70,45 @@ ExcludeList::~ExcludeList()
}
}
+#ifdef WIN32
+std::string ExcludeList::ReplaceSlashesDefinite(const std::string& input) const
+{
+ std::string output = input;
+
+ for (std::string::size_type pos = output.find("/");
+ pos != std::string::npos;
+ pos = output.find("/"))
+ {
+ output.replace(pos, 1, DIRECTORY_SEPARATOR);
+ }
+
+ for (std::string::iterator i = output.begin(); i != output.end(); i++)
+ {
+ *i = tolower(*i);
+ }
+
+ return output;
+}
+
+std::string ExcludeList::ReplaceSlashesRegex(const std::string& input) const
+{
+ std::string output = input;
+
+ for (std::string::size_type pos = output.find("/");
+ pos != std::string::npos;
+ pos = output.find("/"))
+ {
+ output.replace(pos, 1, "\\" DIRECTORY_SEPARATOR);
+ }
+
+ for (std::string::iterator i = output.begin(); i != output.end(); i++)
+ {
+ *i = tolower(*i);
+ }
+
+ return output;
+}
+#endif
// --------------------------------------------------------------------------
//
@@ -88,7 +132,24 @@ void ExcludeList::AddDefiniteEntries(const std::string &rEntries)
{
if(i->size() > 0)
{
- mDefinite.insert(*i);
+ std::string entry = *i;
+
+ // Convert any forward slashes in the string
+ // to backslashes
+
+ #ifdef WIN32
+ entry = ReplaceSlashesDefinite(entry);
+ #endif
+
+ if (entry.size() > 0 && entry[entry.size() - 1] ==
+ DIRECTORY_SEPARATOR_ASCHAR)
+ {
+ BOX_WARNING("Exclude entry ends in path "
+ "separator, will never match: "
+ << entry);
+ }
+
+ mDefinite.insert(entry);
}
}
}
@@ -107,7 +168,7 @@ void ExcludeList::AddDefiniteEntries(const std::string &rEntries)
// --------------------------------------------------------------------------
void ExcludeList::AddRegexEntries(const std::string &rEntries)
{
-#ifdef HAVE_REGEX_H
+#ifdef HAVE_REGEX_SUPPORT
// Split strings up
std::vector<std::string> ens;
@@ -123,8 +184,18 @@ void ExcludeList::AddRegexEntries(const std::string &rEntries)
try
{
+ std::string entry = *i;
+
+ // Convert any forward slashes in the string
+ // to appropriately escaped backslashes
+
+ #ifdef WIN32
+ entry = ReplaceSlashesRegex(entry);
+ #endif
+
// Compile
- if(::regcomp(pregex, i->c_str(), REG_EXTENDED | REG_NOSUB) != 0)
+ if(::regcomp(pregex, entry.c_str(),
+ REG_EXTENDED | REG_NOSUB) != 0)
{
THROW_EXCEPTION(CommonException, BadRegularExpression)
}
@@ -132,7 +203,7 @@ void ExcludeList::AddRegexEntries(const std::string &rEntries)
// Store in list of regular expressions
mRegex.push_back(pregex);
// Store in list of regular expression string for Serialize
- mRegexStr.push_back(i->c_str());
+ mRegexStr.push_back(entry.c_str());
}
catch(...)
{
@@ -158,10 +229,16 @@ void ExcludeList::AddRegexEntries(const std::string &rEntries)
// --------------------------------------------------------------------------
bool ExcludeList::IsExcluded(const std::string &rTest) const
{
+ std::string test = rTest;
+
+ #ifdef WIN32
+ test = ReplaceSlashesDefinite(test);
+ #endif
+
// Check against the always include list
if(mpAlwaysInclude != 0)
{
- if(mpAlwaysInclude->IsExcluded(rTest))
+ if(mpAlwaysInclude->IsExcluded(test))
{
// Because the "always include" list says it's 'excluded'
// this means it should actually be included.
@@ -170,17 +247,17 @@ bool ExcludeList::IsExcluded(const std::string &rTest) const
}
// Is it in the set of definite entries?
- if(mDefinite.find(rTest) != mDefinite.end())
+ if(mDefinite.find(test) != mDefinite.end())
{
return true;
}
// Check against regular expressions
-#ifdef HAVE_REGEX_H
+#ifdef HAVE_REGEX_SUPPORT
for(std::vector<regex_t *>::const_iterator i(mRegex.begin()); i != mRegex.end(); ++i)
{
// Test against this expression
- if(regexec(*i, rTest.c_str(), 0, 0 /* no match information required */, 0 /* no flags */) == 0)
+ if(regexec(*i, test.c_str(), 0, 0 /* no match information required */, 0 /* no flags */) == 0)
{
// match happened
return true;
@@ -232,7 +309,7 @@ void ExcludeList::Deserialize(Archive & rArchive)
//
mDefinite.clear();
-#ifdef HAVE_REGEX_H
+#ifdef HAVE_REGEX_SUPPORT
// free regex memory
while(mRegex.size() > 0)
{
@@ -273,7 +350,7 @@ void ExcludeList::Deserialize(Archive & rArchive)
//
//
//
-#ifdef HAVE_REGEX_H
+#ifdef HAVE_REGEX_SUPPORT
rArchive.Read(iCount);
if (iCount > 0)
@@ -310,7 +387,7 @@ void ExcludeList::Deserialize(Archive & rArchive)
}
}
}
-#endif // HAVE_REGEX_H
+#endif // HAVE_REGEX_SUPPORT
//
//
@@ -365,7 +442,7 @@ void ExcludeList::Serialize(Archive & rArchive) const
//
//
//
-#ifdef HAVE_REGEX_H
+#ifdef HAVE_REGEX_SUPPORT
// don't even try to save compiled regular expressions,
// use string copies instead.
ASSERT(mRegex.size() == mRegexStr.size());
@@ -378,7 +455,7 @@ void ExcludeList::Serialize(Archive & rArchive) const
{
rArchive.Write(*i);
}
-#endif // HAVE_REGEX_H
+#endif // HAVE_REGEX_SUPPORT
//
//
diff --git a/lib/common/ExcludeList.h b/lib/common/ExcludeList.h
index 720b6788..3c41bd11 100644
--- a/lib/common/ExcludeList.h
+++ b/lib/common/ExcludeList.h
@@ -50,7 +50,7 @@ public:
// Mainly for tests
unsigned int SizeOfDefiniteList() const {return mDefinite.size();}
unsigned int SizeOfRegexList() const
-#ifdef HAVE_REGEX_H
+#ifdef HAVE_REGEX_SUPPORT
{return mRegex.size();}
#else
{return 0;}
@@ -58,11 +58,16 @@ public:
private:
std::set<std::string> mDefinite;
-#ifdef HAVE_REGEX_H
+#ifdef HAVE_REGEX_SUPPORT
std::vector<regex_t *> mRegex;
std::vector<std::string> mRegexStr; // save original regular expression string-based source for Serialize
#endif
+#ifdef WIN32
+ std::string ReplaceSlashesDefinite(const std::string& input) const;
+ std::string ReplaceSlashesRegex (const std::string& input) const;
+#endif
+
// For exceptions to the excludes
ExcludeList *mpAlwaysInclude;
};
diff --git a/lib/common/FileStream.cpp b/lib/common/FileStream.cpp
index b6ae083e..e0806e10 100644
--- a/lib/common/FileStream.cpp
+++ b/lib/common/FileStream.cpp
@@ -10,6 +10,9 @@
#include "Box.h"
#include "FileStream.h"
#include "CommonException.h"
+#include "Logging.h"
+
+#include <errno.h>
#include "MemLeakFindOn.h"
@@ -36,7 +39,15 @@ FileStream::FileStream(const char *Filename, int flags, int mode)
#endif
{
MEMLEAKFINDER_NOT_A_LEAK(this);
- THROW_EXCEPTION(CommonException, OSFileOpenError)
+
+ if(errno == EACCES)
+ {
+ THROW_EXCEPTION(CommonException, AccessDenied)
+ }
+ else
+ {
+ THROW_EXCEPTION(CommonException, OSFileOpenError)
+ }
}
#ifdef WIN32
this->fileName = Filename;
@@ -47,7 +58,7 @@ FileStream::FileStream(const char *Filename, int flags, int mode)
// --------------------------------------------------------------------------
//
// Function
-// Name: FileStream::FileStream(int)
+// Name: FileStream::FileStream(tOSFileHandle)
// Purpose: Constructor, using existing file descriptor
// Created: 2003/08/28
//
@@ -63,8 +74,12 @@ FileStream::FileStream(tOSFileHandle FileDescriptor)
#endif
{
MEMLEAKFINDER_NOT_A_LEAK(this);
+ BOX_ERROR("FileStream: called with invalid file handle");
THROW_EXCEPTION(CommonException, OSFileOpenError)
}
+#ifdef WIN32
+ this->fileName = "HANDLE";
+#endif
}
#if 0
@@ -87,6 +102,7 @@ FileStream::FileStream(const FileStream &rToCopy)
#endif
{
MEMLEAKFINDER_NOT_A_LEAK(this);
+ BOX_ERROR("FileStream: copying unopened file");
THROW_EXCEPTION(CommonException, OSFileOpenError)
}
}
@@ -138,8 +154,14 @@ int FileStream::Read(void *pBuffer, int NBytes, int Timeout)
{
r = numBytesRead;
}
+ else if (GetLastError() == ERROR_BROKEN_PIPE)
+ {
+ r = 0;
+ }
else
{
+ BOX_ERROR("Failed to read from file: " <<
+ GetErrorMessage(GetLastError()));
r = -1;
}
#else
@@ -203,7 +225,7 @@ void FileStream::Write(const void *pBuffer, int NBytes)
NULL
);
- if ( (res == 0) || (numBytesWritten != NBytes))
+ if ((res == 0) || (numBytesWritten != (DWORD)NBytes))
{
// DWORD err = GetLastError();
THROW_EXCEPTION(CommonException, OSFileWriteError)
diff --git a/lib/common/Guards.h b/lib/common/Guards.h
index b1bca0fa..d2fb84e0 100644
--- a/lib/common/Guards.h
+++ b/lib/common/Guards.h
@@ -15,12 +15,16 @@
#include <unistd.h>
#endif
+#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/stat.h>
+
#include <new>
#include "CommonException.h"
+#include "Logging.h"
#include "MemLeakFindOn.h"
@@ -28,11 +32,13 @@ template <int flags = O_RDONLY | O_BINARY, int mode = (S_IRUSR | S_IWUSR | S_IRG
class FileHandleGuard
{
public:
- FileHandleGuard(const char *filename)
- : mOSFileHandle(::open(filename, flags, mode))
+ FileHandleGuard(const std::string& rFilename)
+ : mOSFileHandle(::open(rFilename.c_str(), flags, mode))
{
if(mOSFileHandle < 0)
{
+ BOX_ERROR("FileHandleGuard: failed to open file '" <<
+ rFilename << "': " << strerror(errno));
THROW_EXCEPTION(CommonException, OSFileOpenError)
}
}
diff --git a/lib/common/InvisibleTempFileStream.cpp b/lib/common/InvisibleTempFileStream.cpp
new file mode 100644
index 00000000..a7e19ad3
--- /dev/null
+++ b/lib/common/InvisibleTempFileStream.cpp
@@ -0,0 +1,39 @@
+// --------------------------------------------------------------------------
+//
+// File
+// Name: InvisibleTempFileStream.cpp
+// Purpose: IOStream interface to temporary files that
+// delete themselves
+// Created: 2006/10/13
+//
+// --------------------------------------------------------------------------
+
+#include "Box.h"
+#include "InvisibleTempFileStream.h"
+
+#include "MemLeakFindOn.h"
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: InvisibleTempFileStream::InvisibleTempFileStream
+// (const char *, int, int)
+// Purpose: Constructor, opens invisible file
+// Created: 2006/10/13
+//
+// --------------------------------------------------------------------------
+InvisibleTempFileStream::InvisibleTempFileStream(const char *Filename, int flags, int mode)
+#ifdef WIN32
+ : FileStream(::openfile(Filename, flags | O_TEMPORARY, mode))
+#else
+ : FileStream(::open(Filename, flags, mode))
+#endif
+{
+ #ifndef WIN32
+ if(unlink(Filename) != 0)
+ {
+ MEMLEAKFINDER_NOT_A_LEAK(this);
+ THROW_EXCEPTION(CommonException, OSFileOpenError)
+ }
+ #endif
+}
diff --git a/lib/common/InvisibleTempFileStream.h b/lib/common/InvisibleTempFileStream.h
new file mode 100644
index 00000000..a77d05e2
--- /dev/null
+++ b/lib/common/InvisibleTempFileStream.h
@@ -0,0 +1,35 @@
+// --------------------------------------------------------------------------
+//
+// File
+// Name: InvisibleTempFileStream.h
+// Purpose: FileStream interface to temporary files that
+// delete themselves
+// Created: 2006/10/13
+//
+// --------------------------------------------------------------------------
+
+#ifndef INVISIBLETEMPFILESTREAM__H
+#define INVISIBLETEMPFILESTREAM__H
+
+#include "FileStream.h"
+
+class InvisibleTempFileStream : public FileStream
+{
+public:
+ InvisibleTempFileStream(const char *Filename,
+#ifdef WIN32
+ int flags = (O_RDONLY | O_BINARY),
+#else
+ int flags = O_RDONLY,
+#endif
+ int mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH));
+
+private:
+ InvisibleTempFileStream(const InvisibleTempFileStream &rToCopy)
+ : FileStream(INVALID_FILE)
+ { /* do not call */ }
+};
+
+#endif // INVISIBLETEMPFILESTREAM__H
+
+
diff --git a/lib/common/Logging.cpp b/lib/common/Logging.cpp
new file mode 100644
index 00000000..d22db238
--- /dev/null
+++ b/lib/common/Logging.cpp
@@ -0,0 +1,334 @@
+// --------------------------------------------------------------------------
+//
+// File
+// Name: Logging.cpp
+// Purpose: Generic logging core routines implementation
+// Created: 2006/12/16
+//
+// --------------------------------------------------------------------------
+
+#include "Box.h"
+
+#include <errno.h>
+#include <time.h>
+
+#ifdef HAVE_SYSLOG_H
+ #include <syslog.h>
+#endif
+
+#include "Logging.h"
+
+#include <iomanip>
+
+bool Logging::sLogToSyslog = false;
+bool Logging::sLogToConsole = false;
+bool Logging::sContextSet = false;
+
+std::vector<Logger*> Logging::sLoggers;
+std::string Logging::sContext;
+Console* Logging::spConsole = NULL;
+Syslog* Logging::spSyslog = NULL;
+Log::Level Logging::sGlobalLevel = Log::EVERYTHING;
+Logging Logging::sGlobalLogging; //automatic initialisation
+
+Logging::Logging()
+{
+ ASSERT(!spConsole);
+ ASSERT(!spSyslog);
+ spConsole = new Console();
+ spSyslog = new Syslog();
+ sLogToConsole = true;
+ sLogToSyslog = true;
+}
+
+Logging::~Logging()
+{
+ sLogToConsole = false;
+ sLogToSyslog = false;
+ delete spConsole;
+ delete spSyslog;
+ spConsole = NULL;
+ spSyslog = NULL;
+}
+
+void Logging::ToSyslog(bool enabled)
+{
+ if (!sLogToSyslog && enabled)
+ {
+ Add(spSyslog);
+ }
+
+ if (sLogToSyslog && !enabled)
+ {
+ Remove(spSyslog);
+ }
+
+ sLogToSyslog = enabled;
+}
+
+void Logging::ToConsole(bool enabled)
+{
+ if (!sLogToConsole && enabled)
+ {
+ Add(spConsole);
+ }
+
+ if (sLogToConsole && !enabled)
+ {
+ Remove(spConsole);
+ }
+
+ sLogToConsole = enabled;
+}
+
+void Logging::FilterConsole(Log::Level level)
+{
+ spConsole->Filter(level);
+}
+
+void Logging::FilterSyslog(Log::Level level)
+{
+ spSyslog->Filter(level);
+}
+
+void Logging::Add(Logger* pNewLogger)
+{
+ for (std::vector<Logger*>::iterator i = sLoggers.begin();
+ i != sLoggers.end(); i++)
+ {
+ if (*i == pNewLogger)
+ {
+ return;
+ }
+ }
+
+ sLoggers.insert(sLoggers.begin(), pNewLogger);
+}
+
+void Logging::Remove(Logger* pOldLogger)
+{
+ for (std::vector<Logger*>::iterator i = sLoggers.begin();
+ i != sLoggers.end(); i++)
+ {
+ if (*i == pOldLogger)
+ {
+ sLoggers.erase(i);
+ return;
+ }
+ }
+}
+
+void Logging::Log(Log::Level level, const std::string& rFile,
+ int line, const std::string& rMessage)
+{
+ if (level > sGlobalLevel)
+ {
+ return;
+ }
+
+ std::string newMessage;
+
+ if (sContextSet)
+ {
+ newMessage += "[" + sContext + "] ";
+ }
+
+ newMessage += rMessage;
+
+ for (std::vector<Logger*>::iterator i = sLoggers.begin();
+ i != sLoggers.end(); i++)
+ {
+ bool result = (*i)->Log(level, rFile, line, newMessage);
+ if (!result)
+ {
+ return;
+ }
+ }
+}
+
+void Logging::SetContext(std::string context)
+{
+ sContext = context;
+ sContextSet = true;
+}
+
+void Logging::ClearContext()
+{
+ sContextSet = false;
+}
+
+void Logging::SetProgramName(const std::string& rProgramName)
+{
+ for (std::vector<Logger*>::iterator i = sLoggers.begin();
+ i != sLoggers.end(); i++)
+ {
+ (*i)->SetProgramName(rProgramName);
+ }
+}
+
+Logger::Logger()
+: mCurrentLevel(Log::EVERYTHING)
+{
+ Logging::Add(this);
+}
+
+Logger::~Logger()
+{
+ Logging::Remove(this);
+}
+
+bool Console::sShowTime = false;
+bool Console::sShowTag = false;
+std::string Console::sTag;
+
+void Console::SetTag(const std::string& rTag)
+{
+ sTag = rTag;
+ sShowTag = true;
+}
+
+void Console::SetShowTime(bool enabled)
+{
+ sShowTime = enabled;
+}
+
+bool Console::Log(Log::Level level, const std::string& rFile,
+ int line, std::string& rMessage)
+{
+ if (level > GetLevel())
+ {
+ return true;
+ }
+
+ FILE* target = stdout;
+
+ if (level <= Log::WARNING)
+ {
+ target = stderr;
+ }
+
+ std::string msg;
+
+ if (sShowTime)
+ {
+ struct tm time_now, *tm_ptr = &time_now;
+ time_t time_t_now = time(NULL);
+
+ if (time_t_now == ((time_t)-1))
+ {
+ msg += strerror(errno);
+ msg += " ";
+ }
+ #ifdef WIN32
+ else if ((tm_ptr = localtime(&time_t_now)) != NULL)
+ #else
+ else if (localtime_r(&time_t_now, &time_now) != NULL)
+ #endif
+ {
+ std::ostringstream buf;
+ buf << std::setfill('0') <<
+ std::setw(2) << tm_ptr->tm_hour << ":" <<
+ std::setw(2) << tm_ptr->tm_min << ":" <<
+ std::setw(2) << tm_ptr->tm_sec << " ";
+ msg += buf.str();
+ }
+ else
+ {
+ msg += strerror(errno);
+ msg += " ";
+ }
+ }
+
+ if (sShowTag)
+ {
+ msg += "[" + sTag + "] ";
+ }
+
+ if (level <= Log::FATAL)
+ {
+ msg += "FATAL: ";
+ }
+ else if (level <= Log::ERROR)
+ {
+ msg += "ERROR: ";
+ }
+ else if (level <= Log::WARNING)
+ {
+ msg += "WARNING: ";
+ }
+ else if (level <= Log::NOTICE)
+ {
+ msg += "NOTICE: ";
+ }
+
+ msg += rMessage;
+
+ fprintf(target, "%s\n", msg.c_str());
+
+ return true;
+}
+
+bool Syslog::Log(Log::Level level, const std::string& rFile,
+ int line, std::string& rMessage)
+{
+ if (level > GetLevel())
+ {
+ return true;
+ }
+
+ int syslogLevel = LOG_ERR;
+
+ switch(level)
+ {
+ case Log::NOTHING: /* fall through */
+ case Log::FATAL: syslogLevel = LOG_CRIT; break;
+ case Log::ERROR: syslogLevel = LOG_ERR; break;
+ case Log::WARNING: syslogLevel = LOG_WARNING; break;
+ case Log::NOTICE: syslogLevel = LOG_NOTICE; break;
+ case Log::INFO: syslogLevel = LOG_INFO; break;
+ case Log::TRACE: /* fall through */
+ case Log::EVERYTHING: syslogLevel = LOG_DEBUG; break;
+ }
+
+ std::string msg;
+
+ if (level <= Log::FATAL)
+ {
+ msg = "FATAL: ";
+ }
+ else if (level <= Log::ERROR)
+ {
+ msg = "ERROR: ";
+ }
+ else if (level <= Log::WARNING)
+ {
+ msg = "WARNING: ";
+ }
+ else if (level <= Log::NOTICE)
+ {
+ msg = "NOTICE: ";
+ }
+
+ msg += rMessage;
+
+ syslog(syslogLevel, "%s", msg.c_str());
+
+ return true;
+}
+
+Syslog::Syslog()
+{
+ ::openlog("Box Backup", LOG_PID, LOG_LOCAL6);
+}
+
+Syslog::~Syslog()
+{
+ ::closelog();
+}
+
+void Syslog::SetProgramName(const std::string& rProgramName)
+{
+ mName = rProgramName;
+ ::closelog();
+ ::openlog(mName.c_str(), LOG_PID, LOG_LOCAL6);
+}
diff --git a/lib/common/Logging.h b/lib/common/Logging.h
new file mode 100644
index 00000000..9601a495
--- /dev/null
+++ b/lib/common/Logging.h
@@ -0,0 +1,197 @@
+// --------------------------------------------------------------------------
+//
+// File
+// Name: Logging.h
+// Purpose: Generic logging core routines declarations and macros
+// Created: 2006/12/16
+//
+// --------------------------------------------------------------------------
+
+#ifndef LOGGING__H
+#define LOGGING__H
+
+#include <iomanip>
+#include <sstream>
+#include <vector>
+
+/*
+#define BOX_LOG(level, stuff) \
+{ \
+ if(Log::sMaxLoggingLevelForAnyOutput >= level) \
+ std::ostringstream line; \
+ line << stuff; \
+ Log::Write(level, __FILE__, __LINE__, line.str()); \
+ } \
+}
+*/
+
+#define BOX_LOG(level, stuff) \
+{ \
+ std::ostringstream line; \
+ line << stuff; \
+ Logging::Log(level, __FILE__, __LINE__, line.str()); \
+}
+
+#define BOX_FATAL(stuff) BOX_LOG(Log::FATAL, stuff)
+#define BOX_ERROR(stuff) BOX_LOG(Log::ERROR, stuff)
+#define BOX_WARNING(stuff) BOX_LOG(Log::WARNING, stuff)
+#define BOX_NOTICE(stuff) BOX_LOG(Log::NOTICE, stuff)
+#define BOX_INFO(stuff) BOX_LOG(Log::INFO, stuff)
+#if defined NDEBUG && ! defined COMPILE_IN_TRACES
+ #define BOX_TRACE(stuff)
+#else
+ #define BOX_TRACE(stuff) BOX_LOG(Log::TRACE, stuff)
+#endif
+
+#define BOX_FORMAT_ACCOUNT(accno) \
+ std::hex << \
+ std::showbase << \
+ std::internal << \
+ std::setw(10) << \
+ std::setfill('0') << \
+ (accno)
+
+#define BOX_FORMAT_OBJECTID(objectid) \
+ std::hex << \
+ std::showbase << \
+ (objectid)
+
+#undef ERROR
+
+namespace Log
+{
+ enum Level
+ {
+ NOTHING = 1,
+ FATAL,
+ ERROR,
+ WARNING,
+ NOTICE,
+ INFO,
+ TRACE,
+ EVERYTHING
+ };
+}
+
+// --------------------------------------------------------------------------
+//
+// Class
+// Name: Logger
+// Purpose: Abstract base class for log targets
+// Created: 2006/12/16
+//
+// --------------------------------------------------------------------------
+
+class Logger
+{
+ private:
+ Log::Level mCurrentLevel;
+
+ public:
+ Logger();
+ virtual ~Logger();
+
+ virtual bool Log(Log::Level level, const std::string& rFile,
+ int line, std::string& rMessage) = 0;
+
+ void Filter(Log::Level level)
+ {
+ mCurrentLevel = level;
+ }
+
+ virtual const char* GetType() = 0;
+ Log::Level GetLevel() { return mCurrentLevel; }
+
+ virtual void SetProgramName(const std::string& rProgramName) = 0;
+};
+
+// --------------------------------------------------------------------------
+//
+// Class
+// Name: Console
+// Purpose: Console logging target
+// Created: 2006/12/16
+//
+// --------------------------------------------------------------------------
+
+class Console : public Logger
+{
+ private:
+ static bool sShowTime;
+ static bool sShowTag;
+ static std::string sTag;
+
+ public:
+ virtual bool Log(Log::Level level, const std::string& rFile,
+ int line, std::string& rMessage);
+ virtual const char* GetType() { return "Console"; }
+ virtual void SetProgramName(const std::string& rProgramName) { }
+
+ static void SetTag(const std::string& rTag);
+ static void SetShowTime(bool enabled);
+};
+
+// --------------------------------------------------------------------------
+//
+// Class
+// Name: Syslog
+// Purpose: Syslog (or Windows Event Viewer) logging target
+// Created: 2006/12/16
+//
+// --------------------------------------------------------------------------
+
+class Syslog : public Logger
+{
+ private:
+ std::string mName;
+
+ public:
+ Syslog();
+ virtual ~Syslog();
+
+ virtual bool Log(Log::Level level, const std::string& rFile,
+ int line, std::string& rMessage);
+ virtual const char* GetType() { return "Syslog"; }
+ virtual void SetProgramName(const std::string& rProgramName);
+};
+
+// --------------------------------------------------------------------------
+//
+// Class
+// Name: Logging
+// Purpose: Static logging helper, keeps track of enabled loggers
+// and distributes log messages to them.
+// Created: 2006/12/16
+//
+// --------------------------------------------------------------------------
+
+class Logging
+{
+ private:
+ static std::vector<Logger*> sLoggers;
+ static bool sLogToSyslog, sLogToConsole;
+ static std::string sContext;
+ static bool sContextSet;
+ static Console* spConsole;
+ static Syslog* spSyslog;
+ static Log::Level sGlobalLevel;
+ static Logging sGlobalLogging;
+
+ public:
+ Logging ();
+ ~Logging();
+ static void ToSyslog (bool enabled);
+ static void ToConsole (bool enabled);
+ static void FilterSyslog (Log::Level level);
+ static void FilterConsole (Log::Level level);
+ static void Add (Logger* pNewLogger);
+ static void Remove (Logger* pOldLogger);
+ static void Log(Log::Level level, const std::string& rFile,
+ int line, const std::string& rMessage);
+ static void SetContext(std::string context);
+ static void ClearContext();
+ static void SetGlobalLevel(Log::Level level) { sGlobalLevel = level; }
+ static void SetProgramName(const std::string& rProgramName);
+};
+
+#endif // LOGGING__H
diff --git a/lib/common/MainHelper.h b/lib/common/MainHelper.h
index ab75af96..d91bc2f9 100644
--- a/lib/common/MainHelper.h
+++ b/lib/common/MainHelper.h
@@ -17,6 +17,7 @@
#define MAINHELPER_START \
if(argc == 2 && ::strcmp(argv[1], "--version") == 0) \
{ printf(BOX_VERSION "\n"); return 0; } \
+ MEMLEAKFINDER_INIT \
MEMLEAKFINDER_START \
try {
#define MAINHELPER_END \
diff --git a/lib/common/MemLeakFinder.h b/lib/common/MemLeakFinder.h
index f5887dac..450d42f8 100644
--- a/lib/common/MemLeakFinder.h
+++ b/lib/common/MemLeakFinder.h
@@ -10,8 +10,6 @@
#ifndef MEMLEAKFINDER__H
#define MEMLEAKFINDER__H
-#define DEBUG_NEW new(__FILE__,__LINE__)
-
#ifdef MEMLEAKFINDER_FULL_MALLOC_MONITORING
// include stdlib now, to avoid problems with having the macros defined already
#include <stdlib.h>
@@ -20,6 +18,13 @@
// global enable flag
extern bool memleakfinder_global_enable;
+class MemLeakSuppressionGuard
+{
+ public:
+ MemLeakSuppressionGuard();
+ ~MemLeakSuppressionGuard();
+};
+
extern "C"
{
void *memleakfinder_malloc(size_t size, const char *file, int line);
@@ -27,6 +32,8 @@ extern "C"
void memleakfinder_free(void *ptr);
}
+void memleakfinder_init();
+
int memleakfinder_numleaks();
void memleakfinder_reportleaks();
@@ -41,12 +48,9 @@ void memleakfinder_traceblocksinsection();
void memleakfinder_notaleak(void *ptr);
-void *operator new(size_t size, const char *file, int line);
+void *operator new (size_t size, const char *file, int line);
void *operator new[](size_t size, const char *file, int line);
-void operator delete(void *ptr) throw ();
-void operator delete[](void *ptr) throw ();
-
// define the malloc functions now, if required
#ifdef MEMLEAKFINDER_FULL_MALLOC_MONITORING
#define malloc(X) memleakfinder_malloc(X, __FILE__, __LINE__)
@@ -55,6 +59,5 @@ void operator delete[](void *ptr) throw ();
#define MEMLEAKFINDER_MALLOC_MONITORING_DEFINED
#endif
-
#endif // MEMLEAKFINDER__H
diff --git a/lib/common/PartialReadStream.cpp b/lib/common/PartialReadStream.cpp
index 0b5c4cf6..76096738 100644
--- a/lib/common/PartialReadStream.cpp
+++ b/lib/common/PartialReadStream.cpp
@@ -16,13 +16,15 @@
// --------------------------------------------------------------------------
//
// Function
-// Name: PartialReadStream::PartialReadStream(IOStream &, int)
-// Purpose: Constructor, taking another stream and the number of bytes
-// to be read from it.
+// Name: PartialReadStream::PartialReadStream(IOStream &,
+// pos_type)
+// Purpose: Constructor, taking another stream and the number of
+// bytes to be read from it.
// Created: 2003/08/26
//
// --------------------------------------------------------------------------
-PartialReadStream::PartialReadStream(IOStream &rSource, int BytesToRead)
+PartialReadStream::PartialReadStream(IOStream &rSource,
+ pos_type BytesToRead)
: mrSource(rSource),
mBytesLeft(BytesToRead)
{
diff --git a/lib/common/PartialReadStream.h b/lib/common/PartialReadStream.h
index 42cb7aeb..1b46b0bd 100644
--- a/lib/common/PartialReadStream.h
+++ b/lib/common/PartialReadStream.h
@@ -23,7 +23,7 @@
class PartialReadStream : public IOStream
{
public:
- PartialReadStream(IOStream &rSource, int BytesToRead);
+ PartialReadStream(IOStream &rSource, pos_type BytesToRead);
~PartialReadStream();
private:
// no copying allowed
@@ -39,7 +39,7 @@ public:
private:
IOStream &mrSource;
- int mBytesLeft;
+ pos_type mBytesLeft;
};
#endif // PARTIALREADSTREAM__H
diff --git a/lib/common/PathUtils.cpp b/lib/common/PathUtils.cpp
new file mode 100644
index 00000000..924d47d2
--- /dev/null
+++ b/lib/common/PathUtils.cpp
@@ -0,0 +1,34 @@
+// --------------------------------------------------------------------------
+//
+// File
+// Name: PathUtils.cpp
+// Purpose: Platform-independent path manipulation
+// Created: 2007/01/17
+//
+// --------------------------------------------------------------------------
+
+#include "Box.h"
+#include <string>
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: MakeFullPath(const std::string& rDir, const std::string& rFile)
+// Purpose: Combine directory and file name
+// Created: 2006/08/10
+//
+// --------------------------------------------------------------------------
+std::string MakeFullPath(const std::string& rDir, const std::string& rEntry)
+{
+ std::string result(rDir);
+
+ if (result.size() > 0 &&
+ result[result.size()-1] != DIRECTORY_SEPARATOR_ASCHAR)
+ {
+ result += DIRECTORY_SEPARATOR;
+ }
+
+ result += rEntry;
+
+ return result;
+}
diff --git a/lib/common/PathUtils.h b/lib/common/PathUtils.h
new file mode 100644
index 00000000..1cf2e507
--- /dev/null
+++ b/lib/common/PathUtils.h
@@ -0,0 +1,26 @@
+// --------------------------------------------------------------------------
+//
+// File
+// Name: PathUtils.h
+// Purpose: Platform-independent path manipulation
+// Created: 2007/01/17
+//
+// --------------------------------------------------------------------------
+
+#ifndef PATHUTILS_H
+#define PATHUTILS_H
+
+#include <string>
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: MakeFullPath(const std::string& rDir, const std::string& rFile)
+// Purpose: Combine directory and file name
+// Created: 2006/08/10
+//
+// --------------------------------------------------------------------------
+
+std::string MakeFullPath(const std::string& rDir, const std::string& rEntry);
+
+#endif // !PATHUTILS_H
diff --git a/lib/common/ReadGatherStream.cpp b/lib/common/ReadGatherStream.cpp
index 9ccc3a54..f50e6664 100644
--- a/lib/common/ReadGatherStream.cpp
+++ b/lib/common/ReadGatherStream.cpp
@@ -59,8 +59,9 @@ ReadGatherStream::~ReadGatherStream()
//
// Function
// Name: ReadGatherStream::AddComponent(IOStream *)
-// Purpose: Add a component to this stream, returning the index of this component
-// in the internal list. Use this with AddBlock()
+// Purpose: Add a component to this stream, returning the index
+// of this component in the internal list. Use this
+// with AddBlock()
// Created: 10/12/03
//
// --------------------------------------------------------------------------
@@ -145,10 +146,10 @@ int ReadGatherStream::Read(void *pBuffer, int NBytes, int Timeout)
if(mPositionInCurrentBlock < mBlocks[mCurrentBlock].mLength)
{
// Read!
- int s = mBlocks[mCurrentBlock].mLength - mPositionInCurrentBlock;
+ pos_type s = mBlocks[mCurrentBlock].mLength - mPositionInCurrentBlock;
if(s > bytesToRead) s = bytesToRead;
- int r = mComponents[mBlocks[mCurrentBlock].mComponent]->Read(buffer, s, Timeout);
+ pos_type r = mComponents[mBlocks[mCurrentBlock].mComponent]->Read(buffer, s, Timeout);
// update variables
mPositionInCurrentBlock += r;
diff --git a/lib/common/ReadLoggingStream.cpp b/lib/common/ReadLoggingStream.cpp
new file mode 100644
index 00000000..9023f827
--- /dev/null
+++ b/lib/common/ReadLoggingStream.cpp
@@ -0,0 +1,207 @@
+// --------------------------------------------------------------------------
+//
+// File
+// Name: ReadLoggingStream.cpp
+// Purpose: Buffering wrapper around IOStreams
+// Created: 2007/01/16
+//
+// --------------------------------------------------------------------------
+
+#include "Box.h"
+
+#include <string.h>
+
+#include "ReadLoggingStream.h"
+#include "CommonException.h"
+#include "Logging.h"
+
+#include "MemLeakFindOn.h"
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: ReadLoggingStream::ReadLoggingStream(const char *, int, int)
+// Purpose: Constructor, set up buffer
+// Created: 2007/01/16
+//
+// --------------------------------------------------------------------------
+ReadLoggingStream::ReadLoggingStream(IOStream& rSource)
+: mrSource(rSource),
+ mOffset(0),
+ mLength(mrSource.BytesLeftToRead()),
+ mTotalRead(0),
+ mStartTime(GetCurrentBoxTime())
+{ }
+
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: ReadLoggingStream::Read(void *, int)
+// Purpose: Reads bytes from the file
+// Created: 2007/01/16
+//
+// --------------------------------------------------------------------------
+int ReadLoggingStream::Read(void *pBuffer, int NBytes, int Timeout)
+{
+ int numBytesRead = mrSource.Read(pBuffer, NBytes, Timeout);
+
+ if (numBytesRead > 0)
+ {
+ mTotalRead += numBytesRead;
+ mOffset += numBytesRead;
+ }
+
+ if (mLength >= 0 && mTotalRead > 0)
+ {
+ box_time_t timeNow = GetCurrentBoxTime();
+ box_time_t elapsed = timeNow - mStartTime;
+ box_time_t finish = (elapsed * mLength) / mTotalRead;
+ box_time_t remain = finish - elapsed;
+
+ BOX_TRACE("Read " << numBytesRead << " bytes at " << mOffset <<
+ ", " << (mLength - mOffset) << " remain, eta " <<
+ BoxTimeToSeconds(remain) << "s");
+ }
+ else if (mLength >= 0 && mTotalRead == 0)
+ {
+ BOX_TRACE("Read " << numBytesRead << " bytes at " << mOffset <<
+ ", " << (mLength - mOffset) << " remain");
+ }
+ else
+ {
+ BOX_TRACE("Read " << numBytesRead << " bytes at " << mOffset <<
+ ", unknown bytes remaining");
+ }
+
+ return numBytesRead;
+}
+
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: ReadLoggingStream::BytesLeftToRead()
+// Purpose: Returns number of bytes to read (may not be most efficient function ever)
+// Created: 2007/01/16
+//
+// --------------------------------------------------------------------------
+IOStream::pos_type ReadLoggingStream::BytesLeftToRead()
+{
+ return mLength - mOffset;
+}
+
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: ReadLoggingStream::Write(void *, int)
+// Purpose: Writes bytes to the underlying stream (not supported)
+// Created: 2003/07/31
+//
+// --------------------------------------------------------------------------
+void ReadLoggingStream::Write(const void *pBuffer, int NBytes)
+{
+ THROW_EXCEPTION(CommonException, NotSupported);
+}
+
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: ReadLoggingStream::GetPosition()
+// Purpose: Get position in stream
+// Created: 2003/08/21
+//
+// --------------------------------------------------------------------------
+IOStream::pos_type ReadLoggingStream::GetPosition() const
+{
+ return mOffset;
+}
+
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: ReadLoggingStream::Seek(pos_type, int)
+// Purpose: Seeks within file, as lseek, invalidate buffer
+// Created: 2003/07/31
+//
+// --------------------------------------------------------------------------
+void ReadLoggingStream::Seek(IOStream::pos_type Offset, int SeekType)
+{
+ mrSource.Seek(Offset, SeekType);
+
+ switch (SeekType)
+ {
+ case SeekType_Absolute:
+ {
+ // just go there
+ mOffset = Offset;
+ }
+ break;
+
+ case SeekType_Relative:
+ {
+ // Actual underlying file position is
+ // (mBufferSize - mBufferPosition) ahead of us.
+ // Need to subtract that amount from the seek
+ // to seek forward that much less, putting the
+ // real pointer in the right place.
+ mOffset += Offset;
+ }
+ break;
+
+ case SeekType_End:
+ {
+ // Actual underlying file position is
+ // (mBufferSize - mBufferPosition) ahead of us.
+ // Need to add that amount to the seek
+ // to seek backwards that much more, putting the
+ // real pointer in the right place.
+ mOffset = mLength - Offset;
+ }
+ }
+}
+
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: ReadLoggingStream::Close()
+// Purpose: Closes the underlying stream (not needed)
+// Created: 2003/07/31
+//
+// --------------------------------------------------------------------------
+void ReadLoggingStream::Close()
+{
+ THROW_EXCEPTION(CommonException, NotSupported);
+}
+
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: ReadLoggingStream::StreamDataLeft()
+// Purpose: Any data left to write?
+// Created: 2003/08/02
+//
+// --------------------------------------------------------------------------
+bool ReadLoggingStream::StreamDataLeft()
+{
+ return mrSource.StreamDataLeft();
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: ReadLoggingStream::StreamClosed()
+// Purpose: Is the stream closed?
+// Created: 2003/08/02
+//
+// --------------------------------------------------------------------------
+bool ReadLoggingStream::StreamClosed()
+{
+ return mrSource.StreamClosed();
+}
+
diff --git a/lib/common/ReadLoggingStream.h b/lib/common/ReadLoggingStream.h
new file mode 100644
index 00000000..15c3ef48
--- /dev/null
+++ b/lib/common/ReadLoggingStream.h
@@ -0,0 +1,43 @@
+// --------------------------------------------------------------------------
+//
+// File
+// Name: ReadLoggingStream.h
+// Purpose: Wrapper around IOStreams that logs read progress
+// Created: 2007/01/16
+//
+// --------------------------------------------------------------------------
+
+#ifndef READLOGGINGSTREAM__H
+#define READLOGGINGSTREAM__H
+
+#include "IOStream.h"
+#include "BoxTime.h"
+
+class ReadLoggingStream : public IOStream
+{
+private:
+ IOStream& mrSource;
+ IOStream::pos_type mOffset, mLength, mTotalRead;
+ box_time_t mStartTime;
+
+public:
+ ReadLoggingStream(IOStream& rSource);
+
+ virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite);
+ virtual pos_type BytesLeftToRead();
+ virtual void Write(const void *pBuffer, int NBytes);
+ virtual pos_type GetPosition() const;
+ virtual void Seek(IOStream::pos_type Offset, int SeekType);
+ virtual void Close();
+
+ virtual bool StreamDataLeft();
+ virtual bool StreamClosed();
+
+private:
+ ReadLoggingStream(const ReadLoggingStream &rToCopy)
+ : mrSource(rToCopy.mrSource) { /* do not call */ }
+};
+
+#endif // READLOGGINGSTREAM__H
+
+
diff --git a/lib/common/Test.h b/lib/common/Test.h
index 56a6c261..f6704db4 100644
--- a/lib/common/Test.h
+++ b/lib/common/Test.h
@@ -10,27 +10,42 @@
#ifndef TEST__H
#define TEST__H
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <stdlib.h>
+#include <errno.h>
#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
-#include <stdio.h>
#include <string>
extern int failures;
+extern int first_fail_line;
+extern std::string first_fail_file;
+extern std::string bbackupd_args, bbstored_args, bbackupquery_args;
+
+#define TEST_FAIL_WITH_MESSAGE(msg) \
+{ \
+ if (failures == 0) \
+ { \
+ first_fail_file = __FILE__; \
+ first_fail_line = __LINE__; \
+ } \
+ failures++; \
+ printf("FAILURE: " msg " at " __FILE__ "(%d)\n", __LINE__); \
+}
-#define TEST_FAIL_WITH_MESSAGE(msg) {failures++; printf("FAILURE: " msg " at " __FILE__ "(%d)\n", __LINE__);}
-#define TEST_ABORT_WITH_MESSAGE(msg) {failures++; printf("FAILURE: " msg " at " __FILE__ "(%d)\n", __LINE__); return 1;}
+#define TEST_ABORT_WITH_MESSAGE(msg) {TEST_FAIL_WITH_MESSAGE(msg); return 1;}
#define TEST_THAT(condition) {if(!(condition)) TEST_FAIL_WITH_MESSAGE("Condition [" #condition "] failed")}
#define TEST_THAT_ABORTONFAIL(condition) {if(!(condition)) TEST_ABORT_WITH_MESSAGE("Condition [" #condition "] failed")}
-// NOTE: The 0- bit it to allow this to work with stuff which has negative constants for flags (eg ConnectionException)
+// NOTE: The 0- bit is to allow this to work with stuff which has negative constants for flags (eg ConnectionException)
#define TEST_CHECK_THROWS(statement, excepttype, subtype) \
{ \
bool didthrow = false; \
@@ -80,55 +95,74 @@ inline int TestGetFileSize(const char *Filename)
return -1;
}
-inline int RunCommand(const char *pCommandLine)
+inline std::string ConvertPaths(const std::string& rOriginal)
{
#ifdef WIN32
// convert UNIX paths to native
- std::string command;
- for (int i = 0; pCommandLine[i] != 0; i++)
+ std::string converted;
+ for (size_t i = 0; i < rOriginal.size(); i++)
{
- if (pCommandLine[i] == '/')
+ if (rOriginal[i] == '/')
{
- command += '\\';
+ converted += '\\';
}
else
{
- command += pCommandLine[i];
+ converted += rOriginal[i];
}
}
+ return converted;
#else // !WIN32
- std::string command = pCommandLine;
+ return rOriginal;
#endif
+}
- return ::system(command.c_str());
+inline int RunCommand(const std::string& rCommandLine)
+{
+ return ::system(ConvertPaths(rCommandLine).c_str());
}
-inline int LaunchServer(const char *pCommandLine, const char *pidFile)
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+inline bool ServerIsAlive(int pid)
{
- if(RunCommand(pCommandLine) != 0)
+#ifdef WIN32
+ HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, false, pid);
+ if (hProcess == NULL)
{
- printf("Server: %s\n", pCommandLine);
- TEST_FAIL_WITH_MESSAGE("Couldn't start server");
- return -1;
+ if (GetLastError() != ERROR_INVALID_PARAMETER)
+ {
+ printf("Failed to open process %d: error %d\n",
+ pid, (int)GetLastError());
+ }
+ return false;
}
- // time for it to start up
- ::sleep(1);
-
- // read pid file
+ CloseHandle(hProcess);
+ return true;
+#else // !WIN32
+ if(pid == 0) return false;
+ return ::kill(pid, 0) != -1;
+#endif // WIN32
+}
+
+inline int ReadPidFile(const char *pidFile)
+{
if(!TestFileExists(pidFile))
{
- printf("Server: %s\n", pCommandLine);
- TEST_FAIL_WITH_MESSAGE("Server didn't save PID file");
+ TEST_FAIL_WITH_MESSAGE("Server didn't save PID file "
+ "(perhaps one was already running?)");
return -1;
}
- FILE *f = fopen(pidFile, "r");
int pid = -1;
+
+ FILE *f = fopen(pidFile, "r");
if(f == NULL || fscanf(f, "%d", &pid) != 1)
{
- printf("Server: %s (pidfile %s)\n", pCommandLine, pidFile);
TEST_FAIL_WITH_MESSAGE("Couldn't read PID file");
return -1;
}
@@ -137,160 +171,177 @@ inline int LaunchServer(const char *pCommandLine, const char *pidFile)
return pid;
}
-#ifdef WIN32
-
-#include "WinNamedPipeStream.h"
-#include "IOStreamGetLine.h"
-#include "BoxPortsAndFiles.h"
-
-bool SendCommands(const std::string& rCmd)
+inline int LaunchServer(const std::string& rCommandLine, const char *pidFile)
{
- WinNamedPipeStream connection;
+#ifdef WIN32
- try
+ PROCESS_INFORMATION procInfo;
+
+ STARTUPINFO startInfo;
+ startInfo.cb = sizeof(startInfo);
+ startInfo.lpReserved = NULL;
+ startInfo.lpDesktop = NULL;
+ startInfo.lpTitle = NULL;
+ startInfo.dwFlags = 0;
+ startInfo.cbReserved2 = 0;
+ startInfo.lpReserved2 = NULL;
+
+ std::string cmd = ConvertPaths(rCommandLine);
+ CHAR* tempCmd = strdup(cmd.c_str());
+
+ DWORD result = CreateProcess
+ (
+ NULL, // lpApplicationName, naughty!
+ tempCmd, // lpCommandLine
+ NULL, // lpProcessAttributes
+ NULL, // lpThreadAttributes
+ false, // bInheritHandles
+ 0, // dwCreationFlags
+ NULL, // lpEnvironment
+ NULL, // lpCurrentDirectory
+ &startInfo, // lpStartupInfo
+ &procInfo // lpProcessInformation
+ );
+
+ free(tempCmd);
+
+ if (result == 0)
{
- connection.Connect(BOX_NAMED_PIPE_NAME);
- }
- catch(...)
- {
- printf("Failed to connect to daemon control socket.\n");
- return false;
+ DWORD err = GetLastError();
+ printf("Launch failed: %s: error %d\n", rCommandLine.c_str(),
+ (int)err);
+ return -1;
}
- // For receiving data
- IOStreamGetLine getLine(connection);
-
- // Wait for the configuration summary
- std::string configSummary;
- if(!getLine.GetLine(configSummary))
- {
- printf("Failed to receive configuration summary from daemon\n");
- return false;
- }
+ CloseHandle(procInfo.hProcess);
+ CloseHandle(procInfo.hThread);
- // Was the connection rejected by the server?
- if(getLine.IsEOF())
- {
- printf("Server rejected the connection.\n");
- return false;
- }
+#else // !WIN32
- // Decode it
- int autoBackup, updateStoreInterval, minimumFileAge, maxUploadWait;
- if(::sscanf(configSummary.c_str(), "bbackupd: %d %d %d %d",
- &autoBackup, &updateStoreInterval,
- &minimumFileAge, &maxUploadWait) != 4)
+ if(RunCommand(rCommandLine) != 0)
{
- printf("Config summary didn't decode\n");
- return false;
+ printf("Server: %s\n", rCommandLine.c_str());
+ TEST_FAIL_WITH_MESSAGE("Couldn't start server");
+ return -1;
}
- std::string cmds;
- bool expectResponse;
+#endif // WIN32
- if (rCmd != "")
- {
- cmds = rCmd;
- cmds += "\nquit\n";
- expectResponse = true;
- }
- else
+ #ifdef WIN32
+ // on other platforms there is no other way to get
+ // the PID, so a NULL pidFile doesn't make sense.
+
+ if (pidFile == NULL)
{
- cmds = "quit\n";
- expectResponse = false;
+ return (int)procInfo.dwProcessId;
}
-
- connection.Write(cmds.c_str(), cmds.size());
-
- // Read the response
- std::string line;
- bool statusOk = !expectResponse;
+ #endif
+
+ // time for it to start up
+ ::fprintf(stdout, "Starting server: %s\n", rCommandLine.c_str());
+ ::fprintf(stdout, "Waiting for server to start: ");
- while (expectResponse && !getLine.IsEOF() && getLine.GetLine(line))
+ for (int i = 0; i < 15; i++)
{
- // Is this an OK or error line?
- if (line == "ok")
+ if (TestFileExists(pidFile))
{
- statusOk = true;
- }
- else if (line == "error")
- {
- printf("ERROR (%s)\n", rCmd.c_str());
break;
}
- else
+
+ #ifdef WIN32
+ if (!ServerIsAlive((int)procInfo.dwProcessId))
{
- printf("WARNING: Unexpected response to command '%s': "
- "%s", rCmd.c_str(), line.c_str());
+ break;
}
+ #endif
+
+ ::fprintf(stdout, ".");
+ ::fflush(stdout);
+ ::sleep(1);
}
-
- return statusOk;
-}
-inline bool ServerIsAlive()
-{
- return SendCommands("");
-}
+ #ifdef WIN32
+ // on Win32 we can check whether the process is alive
+ // without even checking the PID file
-inline bool HUPServer(int pid)
-{
- return SendCommands("reload");
-}
+ if (!ServerIsAlive((int)procInfo.dwProcessId))
+ {
+ ::fprintf(stdout, "server died!\n");
+ TEST_FAIL_WITH_MESSAGE("Server died!");
+ return -1;
+ }
+ #endif
-inline bool KillServer(int pid)
-{
- TEST_THAT(SendCommands("terminate"));
+ if (!TestFileExists(pidFile))
+ {
+ ::fprintf(stdout, "timed out!\n");
+ TEST_FAIL_WITH_MESSAGE("Server didn't save PID file");
+ return -1;
+ }
+
+ ::fprintf(stdout, "done.\n");
+
+ // wait a second for the pid to be written to the file
::sleep(1);
- return !ServerIsAlive();
-}
-#else // !WIN32
+ // read pid file
+ int pid = ReadPidFile(pidFile);
-inline bool ServerIsAlive(int pid)
-{
- if(pid == 0) return false;
- return ::kill(pid, 0) != -1;
-}
+ #ifdef WIN32
+ // On Win32 we can check whether the PID in the pidFile matches
+ // the one returned by the system, which it always should.
-inline bool HUPServer(int pid)
-{
- if(pid == 0) return false;
- return ::kill(pid, SIGHUP) != -1;
-}
+ if (pid != (int)procInfo.dwProcessId)
+ {
+ printf("Server wrote wrong pid to file (%s): expected %d "
+ "but found %d\n", pidFile,
+ (int)procInfo.dwProcessId, pid);
+ TEST_FAIL_WITH_MESSAGE("Server wrote wrong pid to file");
+ return -1;
+ }
+ #endif
-inline bool KillServer(int pid)
-{
- if(pid == 0 || pid == -1) return false;
- bool KilledOK = ::kill(pid, SIGTERM) != -1;
- TEST_THAT(KilledOK);
- ::sleep(1);
- return !ServerIsAlive(pid);
+ return pid;
}
-#endif // WIN32
+#define TestRemoteProcessMemLeaks(filename) \
+ TestRemoteProcessMemLeaksFunc(filename, __FILE__, __LINE__)
-inline void TestRemoteProcessMemLeaks(const char *filename)
+inline void TestRemoteProcessMemLeaksFunc(const char *filename,
+ const char* file, int line)
{
#ifdef BOX_MEMORY_LEAK_TESTING
// Does the file exist?
if(!TestFileExists(filename))
{
+ if (failures == 0)
+ {
+ first_fail_file = file;
+ first_fail_line = line;
+ }
++failures;
- printf("FAILURE: MemLeak report not available (file %s)\n", filename);
+ printf("FAILURE: MemLeak report not available (file %s) "
+ "at %s:%d\n", filename, file, line);
}
else
{
// Is it empty?
if(TestGetFileSize(filename) > 0)
{
+ if (failures == 0)
+ {
+ first_fail_file = file;
+ first_fail_line = line;
+ }
++failures;
- printf("FAILURE: Memory leaks found in other process (file %s)\n==========\n", filename);
+ printf("FAILURE: Memory leaks found in other process "
+ "(file %s) at %s:%d\n==========\n",
+ filename, file, line);
FILE *f = fopen(filename, "r");
- char line[512];
- while(::fgets(line, sizeof(line), f) != 0)
+ char linebuf[512];
+ while(::fgets(linebuf, sizeof(linebuf), f) != 0)
{
- printf("%s", line);
+ printf("%s", linebuf);
}
fclose(f);
printf("==========\n");
@@ -302,5 +353,67 @@ inline void TestRemoteProcessMemLeaks(const char *filename)
#endif
}
-#endif // TEST__H
+#ifdef WIN32
+#define BBACKUPCTL "..\\..\\bin\\bbackupctl\\bbackupctl.exe"
+#define BBACKUPD "..\\..\\bin\\bbackupd\\bbackupd.exe"
+#define BBSTORED "..\\..\\bin\\bbstored\\bbstored.exe"
+#define BBACKUPQUERY "..\\..\\bin\\bbackupquery\\bbackupquery.exe"
+#define BBSTOREACCOUNTS "..\\..\\bin\\bbstoreaccounts\\bbstoreaccounts.exe"
+#define TEST_RETURN(actual, expected) TEST_THAT(actual == expected);
+#else
+#define BBACKUPCTL "../../bin/bbackupctl/bbackupctl"
+#define BBACKUPD "../../bin/bbackupd/bbackupd"
+#define BBSTORED "../../bin/bbstored/bbstored"
+#define BBACKUPQUERY "../../bin/bbackupquery/bbackupquery"
+#define BBSTOREACCOUNTS "../../bin/bbstoreaccounts/bbstoreaccounts"
+#define TEST_RETURN(actual, expected) TEST_THAT(actual == expected*256);
+#endif
+
+inline void terminate_bbackupd(int pid)
+{
+ TEST_THAT(::system(BBACKUPCTL " -q -c testfiles/bbackupd.conf "
+ "terminate") == 0);
+ TestRemoteProcessMemLeaks("bbackupctl.memleaks");
+
+ for (int i = 0; i < 20; i++)
+ {
+ if (!ServerIsAlive(pid)) break;
+ fprintf(stdout, ".");
+ fflush(stdout);
+ sleep(1);
+ }
+
+ TEST_THAT(!ServerIsAlive(pid));
+ TestRemoteProcessMemLeaks("bbackupd.memleaks");
+}
+
+
+// Wait a given number of seconds for something to complete
+inline void wait_for_operation(int seconds)
+{
+ printf("waiting: ");
+ fflush(stdout);
+ for(int l = 0; l < seconds; ++l)
+ {
+ sleep(1);
+ printf(".");
+ fflush(stdout);
+ }
+ printf("\n");
+ fflush(stdout);
+}
+
+inline void safe_sleep(int seconds)
+{
+#ifdef WIN32
+ Sleep(seconds * 1000);
+#else
+ struct timespec ts;
+ ts.tv_sec = seconds;
+ ts.tv_nsec = 0;
+ while (nanosleep(&ts, &ts) == -1 && errno == EINTR)
+ { /* sleep again */ }
+#endif
+}
+#endif // TEST__H
diff --git a/lib/common/Timer.cpp b/lib/common/Timer.cpp
new file mode 100644
index 00000000..a032f330
--- /dev/null
+++ b/lib/common/Timer.cpp
@@ -0,0 +1,386 @@
+// --------------------------------------------------------------------------
+//
+// File
+// Name: Timer.cpp
+// Purpose: Generic timers which execute arbitrary code when
+// they expire.
+// Created: 5/11/2006
+//
+// --------------------------------------------------------------------------
+
+#include "Box.h"
+
+#include <signal.h>
+
+#include "Timer.h"
+#include "Logging.h"
+
+#include "MemLeakFindOn.h"
+
+std::vector<Timer*>* Timers::spTimers = NULL;
+bool Timers::sRescheduleNeeded = false;
+
+typedef void (*sighandler_t)(int);
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: static void Timers::Init()
+// Purpose: Initialise timers, prepare signal handler
+// Created: 5/11/2006
+//
+// --------------------------------------------------------------------------
+void Timers::Init()
+{
+ ASSERT(!spTimers);
+
+ #if defined WIN32 && ! defined PLATFORM_CYGWIN
+ // no support for signals at all
+ InitTimer();
+ SetTimerHandler(Timers::SignalHandler);
+ #else
+ sighandler_t oldHandler = ::sigset(SIGALRM,
+ Timers::SignalHandler);
+ ASSERT(oldHandler == 0);
+ #endif // WIN32 && !PLATFORM_CYGWIN
+
+ spTimers = new std::vector<Timer*>;
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: static void Timers::Cleanup()
+// Purpose: Clean up timers, stop signal handler
+// Created: 6/11/2006
+//
+// --------------------------------------------------------------------------
+void Timers::Cleanup()
+{
+ ASSERT(spTimers);
+
+ #if defined WIN32 && ! defined PLATFORM_CYGWIN
+ // no support for signals at all
+ FiniTimer();
+ SetTimerHandler(NULL);
+ #else
+ struct itimerval timeout;
+ memset(&timeout, 0, sizeof(timeout));
+
+ int result = ::setitimer(ITIMER_REAL, &timeout, NULL);
+ ASSERT(result == 0);
+
+ sighandler_t oldHandler = ::sigset(SIGALRM, NULL);
+ ASSERT(oldHandler == Timers::SignalHandler);
+ #endif // WIN32 && !PLATFORM_CYGWIN
+
+ spTimers->clear();
+ delete spTimers;
+ spTimers = NULL;
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: static void Timers::Add(Timer&)
+// Purpose: Add a new timer to the set, and reschedule next wakeup
+// Created: 5/11/2006
+//
+// --------------------------------------------------------------------------
+void Timers::Add(Timer& rTimer)
+{
+ ASSERT(spTimers);
+ ASSERT(&rTimer);
+ spTimers->push_back(&rTimer);
+ Reschedule();
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: static void Timers::Remove(Timer&)
+// Purpose: Removes the timer from the set (preventing it from
+// being called) and reschedule next wakeup
+// Created: 5/11/2006
+//
+// --------------------------------------------------------------------------
+void Timers::Remove(Timer& rTimer)
+{
+ ASSERT(spTimers);
+ ASSERT(&rTimer);
+
+ bool restart = true;
+ while (restart)
+ {
+ restart = false;
+
+ for (std::vector<Timer*>::iterator i = spTimers->begin();
+ i != spTimers->end(); i++)
+ {
+ if (&rTimer == *i)
+ {
+ spTimers->erase(i);
+ restart = true;
+ break;
+ }
+ }
+ }
+
+ Reschedule();
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: static void Timers::Reschedule()
+// Purpose: Recalculate when the next wakeup is due
+// Created: 5/11/2006
+//
+// --------------------------------------------------------------------------
+void Timers::Reschedule()
+{
+ ASSERT(spTimers);
+ if (spTimers == NULL)
+ {
+ THROW_EXCEPTION(CommonException, Internal)
+ }
+
+ #ifndef WIN32
+ void (*oldhandler)(int) = ::sigset(SIGALRM, Timers::SignalHandler);
+ if (oldhandler != Timers::SignalHandler)
+ {
+ printf("Signal handler was %p, expected %p\n",
+ oldhandler, Timers::SignalHandler);
+ THROW_EXCEPTION(CommonException, Internal)
+ }
+ #endif
+
+ // Clear the reschedule-needed flag to false before we start.
+ // If a timer event occurs while we are scheduling, then we
+ // may or may not need to reschedule again, but this way
+ // we will do it anyway.
+ sRescheduleNeeded = false;
+
+ box_time_t timeNow = GetCurrentBoxTime();
+
+ // scan for, trigger and remove expired timers. Removal requires
+ // us to restart the scan each time, due to std::vector semantics.
+ bool restart = true;
+ while (restart)
+ {
+ restart = false;
+
+ for (std::vector<Timer*>::iterator i = spTimers->begin();
+ i != spTimers->end(); i++)
+ {
+ Timer& rTimer = **i;
+ int64_t timeToExpiry = rTimer.GetExpiryTime() - timeNow;
+
+ if (timeToExpiry <= 0)
+ {
+ BOX_TRACE((int)(timeNow / 1000000) << "." <<
+ (int)(timeNow % 1000000) <<
+ ": timer " << *i << " has expired, "
+ "triggering it");
+ rTimer.OnExpire();
+ spTimers->erase(i);
+ restart = true;
+ break;
+ }
+ else
+ {
+ BOX_TRACE((int)(timeNow / 1000000) << "." <<
+ (int)(timeNow % 1000000) <<
+ ": timer " << *i << " has not "
+ "expired, triggering in " <<
+ (int)(timeToExpiry / 1000000) << "." <<
+ (int)(timeToExpiry % 1000000) <<
+ " seconds");
+ }
+ }
+ }
+
+ // Now the only remaining timers should all be in the future.
+ // Scan to find the next one to fire (earliest deadline).
+
+ int64_t timeToNextEvent = 0;
+
+ for (std::vector<Timer*>::iterator i = spTimers->begin();
+ i != spTimers->end(); i++)
+ {
+ Timer& rTimer = **i;
+ int64_t timeToExpiry = rTimer.GetExpiryTime() - timeNow;
+
+ if (timeToExpiry <= 0)
+ {
+ timeToExpiry = 1;
+ }
+
+ if (timeToNextEvent == 0 || timeToNextEvent > timeToExpiry)
+ {
+ timeToNextEvent = timeToExpiry;
+ }
+ }
+
+ ASSERT(timeToNextEvent >= 0);
+
+ struct itimerval timeout;
+ memset(&timeout, 0, sizeof(timeout));
+
+ timeout.it_value.tv_sec = BoxTimeToSeconds(timeToNextEvent);
+ timeout.it_value.tv_usec = (int)
+ (BoxTimeToMicroSeconds(timeToNextEvent) % MICRO_SEC_IN_SEC);
+
+ if(::setitimer(ITIMER_REAL, &timeout, NULL) != 0)
+ {
+ BOX_ERROR("Failed to initialise timer\n");
+ THROW_EXCEPTION(CommonException, Internal)
+ }
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: static void Timers::SignalHandler(unused)
+// Purpose: Called as signal handler. Nothing is safe in a signal
+// handler, not even traversing the list of timers, so
+// just request a reschedule in future, which will do
+// that for us, and trigger any expired timers at that
+// time.
+// Created: 5/11/2006
+//
+// --------------------------------------------------------------------------
+void Timers::SignalHandler(int iUnused)
+{
+ // ASSERT(spTimers);
+ Timers::RequestReschedule();
+}
+
+Timer::Timer(size_t timeoutSecs)
+: mExpires(GetCurrentBoxTime() + SecondsToBoxTime(timeoutSecs)),
+ mExpired(false)
+{
+ #if !defined NDEBUG && !defined WIN32
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ if (timeoutSecs == 0)
+ {
+ BOX_TRACE(tv.tv_sec << "." << tv.tv_usec <<
+ ": timer " << this << " initialised for " <<
+ timeoutSecs << " secs, will not fire");
+ }
+ else
+ {
+ BOX_TRACE(tv.tv_sec << "." << tv.tv_usec <<
+ ": timer " << this << " initialised for " <<
+ timeoutSecs << " secs, to fire at " <<
+ (int)(mExpires / 1000000) << "." <<
+ (int)(mExpires % 1000000));
+ }
+ #endif
+
+ if (timeoutSecs == 0)
+ {
+ mExpires = 0;
+ }
+ else
+ {
+ Timers::Add(*this);
+ }
+}
+
+Timer::~Timer()
+{
+ #if !defined NDEBUG && !defined WIN32
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ BOX_TRACE(tv.tv_sec << "." << tv.tv_usec <<
+ ": timer " << this << " destroyed");
+ #endif
+
+ Timers::Remove(*this);
+}
+
+Timer::Timer(const Timer& rToCopy)
+: mExpires(rToCopy.mExpires),
+ mExpired(rToCopy.mExpired)
+{
+ #if !defined NDEBUG && !defined WIN32
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ if (mExpired)
+ {
+ BOX_TRACE(tv.tv_sec << "." << tv.tv_usec <<
+ ": timer " << this << " initialised from timer " <<
+ &rToCopy << ", already expired, will not fire");
+ }
+ else if (mExpires == 0)
+ {
+ BOX_TRACE(tv.tv_sec << "." << tv.tv_usec <<
+ ": timer " << this << " initialised from timer " <<
+ &rToCopy << ", no expiry, will not fire");
+ }
+ else
+ {
+ BOX_TRACE(tv.tv_sec << "." << tv.tv_usec <<
+ ": timer " << this << " initialised from timer " <<
+ &rToCopy << " to fire at " <<
+ (int)(mExpires / 1000000) << "." <<
+ (int)(mExpires % 1000000));
+ }
+ #endif
+
+ if (!mExpired && mExpires != 0)
+ {
+ Timers::Add(*this);
+ }
+}
+
+Timer& Timer::operator=(const Timer& rToCopy)
+{
+ #if !defined NDEBUG && !defined WIN32
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ if (rToCopy.mExpired)
+ {
+ BOX_TRACE(tv.tv_sec << "." << tv.tv_usec <<
+ ": timer " << this << " initialised from timer " <<
+ &rToCopy << ", already expired, will not fire");
+ }
+ else if (rToCopy.mExpires == 0)
+ {
+ BOX_TRACE(tv.tv_sec << "." << tv.tv_usec <<
+ ": timer " << this << " initialised from timer " <<
+ &rToCopy << ", no expiry, will not fire");
+ }
+ else
+ {
+ BOX_TRACE(tv.tv_sec << "." << tv.tv_usec <<
+ ": timer " << this << " initialised from timer " <<
+ &rToCopy << " to fire at " <<
+ (int)(rToCopy.mExpires / 1000000) << "." <<
+ (int)(rToCopy.mExpires % 1000000));
+ }
+ #endif
+
+ Timers::Remove(*this);
+ mExpires = rToCopy.mExpires;
+ mExpired = rToCopy.mExpired;
+ if (!mExpired && mExpires != 0)
+ {
+ Timers::Add(*this);
+ }
+ return *this;
+}
+
+void Timer::OnExpire()
+{
+ #if !defined NDEBUG && !defined WIN32
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ BOX_TRACE(tv.tv_sec << "." << tv.tv_usec <<
+ ": timer " << this << " fired");
+ #endif
+
+ mExpired = true;
+}
diff --git a/lib/common/Timer.h b/lib/common/Timer.h
new file mode 100644
index 00000000..ba6d71f4
--- /dev/null
+++ b/lib/common/Timer.h
@@ -0,0 +1,87 @@
+// --------------------------------------------------------------------------
+//
+// File
+// Name: Timer.h
+// Purpose: Generic timers which execute arbitrary code when
+// they expire.
+// Created: 5/11/2006
+//
+// --------------------------------------------------------------------------
+
+#ifndef TIMER__H
+#define TIMER__H
+
+#ifdef HAVE_SYS_TIME_H
+ #include <sys/time.h>
+#endif
+
+#include <vector>
+
+#include "BoxTime.h"
+
+#include "MemLeakFindOn.h"
+
+class Timer;
+
+// --------------------------------------------------------------------------
+//
+// Class
+// Name: Timers
+// Purpose: Static class to manage all timers and arrange
+// efficient delivery of wakeup signals
+// Created: 19/3/04
+//
+// --------------------------------------------------------------------------
+class Timers
+{
+ private:
+ static std::vector<Timer*>* spTimers;
+ static void Reschedule();
+
+ static bool sRescheduleNeeded;
+ static void SignalHandler(int iUnused);
+
+ public:
+ static void Init();
+ static void Cleanup();
+ static void Add (Timer& rTimer);
+ static void Remove(Timer& rTimer);
+ static void RequestReschedule()
+ {
+ sRescheduleNeeded = true;
+ }
+
+ static void RescheduleIfNeeded()
+ {
+ if (sRescheduleNeeded)
+ {
+ Reschedule();
+ }
+ }
+};
+
+class Timer
+{
+public:
+ Timer(size_t timeoutSecs);
+ virtual ~Timer();
+ Timer(const Timer &);
+ Timer &operator=(const Timer &);
+
+public:
+ box_time_t GetExpiryTime() { return mExpires; }
+ virtual void OnExpire();
+ bool HasExpired()
+ {
+ Timers::RescheduleIfNeeded();
+ return mExpired;
+ }
+
+private:
+ box_time_t mExpires;
+ bool mExpired;
+};
+
+#include "MemLeakFindOff.h"
+
+#endif // TIMER__H
diff --git a/lib/common/UnixUser.cpp b/lib/common/UnixUser.cpp
index 5419d8f8..f81b474c 100644
--- a/lib/common/UnixUser.cpp
+++ b/lib/common/UnixUser.cpp
@@ -69,7 +69,7 @@ UnixUser::UnixUser(uid_t UID, gid_t GID)
//
// Function
// Name: UnixUser::~UnixUser()
-// Purpose: Destructor -- reverts to previous user if the change wasn't permanant
+// Purpose: Destructor -- reverts to previous user if the change wasn't perminant
// Created: 21/1/04
//
// --------------------------------------------------------------------------
@@ -78,8 +78,7 @@ UnixUser::~UnixUser()
if(mRevertOnDestruction)
{
// Revert to "real" user and group id of the process
- if(::setegid(::getgid()) != 0
- || ::seteuid(::getuid()) != 0)
+ if(::setegid(::getgid()) != 0 || ::seteuid(::getuid()) != 0)
{
THROW_EXCEPTION(CommonException, CouldNotRestoreProcessUser)
}
@@ -101,8 +100,7 @@ void UnixUser::ChangeProcessUser(bool Temporary)
if(Temporary)
{
// Change temporarily (change effective only)
- if(::setegid(mGID) != 0
- || ::seteuid(mUID) != 0)
+ if(::setegid(mGID) != 0 || ::seteuid(mUID) != 0)
{
THROW_EXCEPTION(CommonException, CouldNotChangeProcessUser)
}
@@ -113,8 +111,7 @@ void UnixUser::ChangeProcessUser(bool Temporary)
else
{
// Change permanently (change all UIDs and GIDs)
- if(::setgid(mGID) != 0
- || ::setuid(mUID) != 0)
+ if(::setgid(mGID) != 0 || ::setuid(mUID) != 0)
{
THROW_EXCEPTION(CommonException, CouldNotChangeProcessUser)
}
diff --git a/lib/common/Utils.cpp b/lib/common/Utils.cpp
index 63d45b5a..5dc03124 100644
--- a/lib/common/Utils.cpp
+++ b/lib/common/Utils.cpp
@@ -20,6 +20,7 @@
#include "Utils.h"
#include "CommonException.h"
+#include "Logging.h"
#include "MemLeakFindOn.h"
@@ -71,14 +72,16 @@ void DumpStackBacktrace()
size = backtrace (array, 10);
strings = backtrace_symbols (array, size);
- printf ("Obtained %zd stack frames.\n", size);
+ BOX_TRACE("Obtained " << size << " stack frames.");
for(i = 0; i < size; i++)
- printf("%s\n", strings[i]);
+ {
+ BOX_TRACE(strings[i]);
+ }
-#ifndef MEMLEAKFINDER_MALLOC_MONITORING_DEFINED
+#include "MemLeakFindOff.h"
free (strings);
-#endif
+#include "MemLeakFindOn.h"
}
#endif
diff --git a/lib/common/ZeroStream.cpp b/lib/common/ZeroStream.cpp
new file mode 100644
index 00000000..9d87d76a
--- /dev/null
+++ b/lib/common/ZeroStream.cpp
@@ -0,0 +1,170 @@
+// --------------------------------------------------------------------------
+//
+// File
+// Name: ZeroStream.cpp
+// Purpose: An IOStream which returns all zeroes up to a certain size
+// Created: 2007/04/28
+//
+// --------------------------------------------------------------------------
+
+#include "Box.h"
+#include "ZeroStream.h"
+#include "CommonException.h"
+
+#include <string.h>
+
+#include "MemLeakFindOn.h"
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: ZeroStream::ZeroStream(IOStream::pos_type)
+// Purpose: Constructor
+// Created: 2007/04/28
+//
+// --------------------------------------------------------------------------
+ZeroStream::ZeroStream(IOStream::pos_type size)
+: mSize(size), mPosition(0)
+{ }
+
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: ZeroStream::Read(void *, int)
+// Purpose: Reads bytes from the file
+// Created: 2007/01/16
+//
+// --------------------------------------------------------------------------
+int ZeroStream::Read(void *pBuffer, int NBytes, int Timeout)
+{
+ ASSERT(NBytes > 0);
+
+ int bytesToRead = NBytes;
+
+ if (bytesToRead > mSize - mPosition)
+ {
+ bytesToRead = mSize - mPosition;
+ }
+
+ memset(pBuffer, 0, bytesToRead);
+ mPosition += bytesToRead;
+
+ return bytesToRead;
+}
+
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: ZeroStream::BytesLeftToRead()
+// Purpose: Returns number of bytes to read (may not be most efficient function ever)
+// Created: 2007/01/16
+//
+// --------------------------------------------------------------------------
+IOStream::pos_type ZeroStream::BytesLeftToRead()
+{
+ return mSize - mPosition;
+}
+
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: ZeroStream::Write(void *, int)
+// Purpose: Writes bytes to the underlying stream (not supported)
+// Created: 2003/07/31
+//
+// --------------------------------------------------------------------------
+void ZeroStream::Write(const void *pBuffer, int NBytes)
+{
+ THROW_EXCEPTION(CommonException, NotSupported);
+}
+
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: ZeroStream::GetPosition()
+// Purpose: Get position in stream
+// Created: 2003/08/21
+//
+// --------------------------------------------------------------------------
+IOStream::pos_type ZeroStream::GetPosition() const
+{
+ return mPosition;
+}
+
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: ZeroStream::Seek(pos_type, int)
+// Purpose: Seeks within file, as lseek, invalidate buffer
+// Created: 2003/07/31
+//
+// --------------------------------------------------------------------------
+void ZeroStream::Seek(IOStream::pos_type Offset, int SeekType)
+{
+ switch (SeekType)
+ {
+ case SeekType_Absolute:
+ {
+ mPosition = Offset;
+ }
+ break;
+
+ case SeekType_Relative:
+ {
+ mPosition += Offset;
+ }
+ break;
+
+ case SeekType_End:
+ {
+ mPosition = mSize - Offset;
+ }
+ }
+}
+
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: ZeroStream::Close()
+// Purpose: Closes the underlying stream (not needed)
+// Created: 2003/07/31
+//
+// --------------------------------------------------------------------------
+void ZeroStream::Close()
+{
+ THROW_EXCEPTION(CommonException, NotSupported);
+}
+
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: ZeroStream::StreamDataLeft()
+// Purpose: Any data left to write?
+// Created: 2003/08/02
+//
+// --------------------------------------------------------------------------
+bool ZeroStream::StreamDataLeft()
+{
+ return false;
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: ZeroStream::StreamClosed()
+// Purpose: Is the stream closed?
+// Created: 2003/08/02
+//
+// --------------------------------------------------------------------------
+bool ZeroStream::StreamClosed()
+{
+ return false;
+}
+
diff --git a/lib/common/ZeroStream.h b/lib/common/ZeroStream.h
new file mode 100644
index 00000000..0119045b
--- /dev/null
+++ b/lib/common/ZeroStream.h
@@ -0,0 +1,39 @@
+// --------------------------------------------------------------------------
+//
+// File
+// Name: ZeroStream.h
+// Purpose: An IOStream which returns all zeroes up to a certain size
+// Created: 2007/04/28
+//
+// --------------------------------------------------------------------------
+
+#ifndef ZEROSTREAM__H
+#define ZEROSTREAM__H
+
+#include "IOStream.h"
+
+class ZeroStream : public IOStream
+{
+private:
+ IOStream::pos_type mSize, mPosition;
+
+public:
+ ZeroStream(IOStream::pos_type mSize);
+
+ virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite);
+ virtual pos_type BytesLeftToRead();
+ virtual void Write(const void *pBuffer, int NBytes);
+ virtual pos_type GetPosition() const;
+ virtual void Seek(IOStream::pos_type Offset, int SeekType);
+ virtual void Close();
+
+ virtual bool StreamDataLeft();
+ virtual bool StreamClosed();
+
+private:
+ ZeroStream(const ZeroStream &rToCopy);
+};
+
+#endif // ZEROSTREAM__H
+
+
diff --git a/lib/crypto/Random.cpp b/lib/crypto/Random.cpp
index 30049ff7..1d6a07f0 100644
--- a/lib/crypto/Random.cpp
+++ b/lib/crypto/Random.cpp
@@ -34,7 +34,8 @@ void Random::Initialise()
THROW_EXCEPTION(CipherException, RandomInitFailed)
}
#else
- ::fprintf(stderr, "No random device -- additional seeding of random number generator not performed.\n");
+ BOX_ERROR("No random device -- additional seeding of random number "
+ "generator not performed.");
#endif
}
diff --git a/lib/intercept/intercept.cpp b/lib/intercept/intercept.cpp
new file mode 100644
index 00000000..a93a5a3b
--- /dev/null
+++ b/lib/intercept/intercept.cpp
@@ -0,0 +1,495 @@
+// --------------------------------------------------------------------------
+//
+// File
+// Name: intercept.cpp
+// Purpose: Syscall interception code for the raidfile test
+// Created: 2003/07/22
+//
+// --------------------------------------------------------------------------
+
+#include "Box.h"
+
+#include "intercept.h"
+
+#ifdef HAVE_SYS_SYSCALL_H
+ #include <sys/syscall.h>
+#endif
+#include <sys/types.h>
+#include <unistd.h>
+
+#ifdef HAVE_SYS_UIO_H
+ #include <sys/uio.h>
+#endif
+
+#include <errno.h>
+
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#ifndef PLATFORM_CLIB_FNS_INTERCEPTION_IMPOSSIBLE
+
+#if !defined(HAVE_SYSCALL) && !defined(HAVE___SYSCALL) && !defined(HAVE___SYSCALL_NEED_DEFN)
+ #define PLATFORM_NO_SYSCALL
+#endif
+
+#ifdef PLATFORM_NO_SYSCALL
+ // For some reason, syscall just doesn't work on Darwin
+ // so instead, we build functions using assembler in a varient
+ // of the technique used in the Darwin Libc
+ extern "C" int
+ TEST_open(const char *path, int flags, mode_t mode);
+ extern "C" int
+ TEST_close(int d);
+ extern "C" ssize_t
+ TEST_write(int d, const void *buf, size_t nbytes);
+ extern "C" ssize_t
+ TEST_read(int d, void *buf, size_t nbytes);
+ extern "C" ssize_t
+ TEST_readv(int d, const struct iovec *iov, int iovcnt);
+ extern "C" off_t
+ TEST_lseek(int fildes, off_t offset, int whence);
+#else
+ #ifdef HAVE___SYSCALL_NEED_DEFN
+ // Need this, not declared in syscall.h nor unistd.h
+ extern "C" off_t __syscall(quad_t number, ...);
+ #endif
+ #ifndef HAVE_SYSCALL
+ #undef syscall
+ #define syscall __syscall
+ #endif
+#endif
+
+#include <string.h>
+#include <stdio.h>
+
+#include "MemLeakFindOn.h"
+
+int intercept_count = 0;
+const char *intercept_filename = 0;
+int intercept_filedes = -1;
+off_t intercept_errorafter = 0;
+int intercept_errno = 0;
+int intercept_syscall = 0;
+off_t intercept_filepos = 0;
+int intercept_delay_ms = 0;
+
+#define SIZE_ALWAYS_ERROR -773
+
+void intercept_clear_setup()
+{
+ intercept_count = 0;
+ intercept_filename = 0;
+ intercept_filedes = -1;
+ intercept_errorafter = 0;
+ intercept_syscall = 0;
+ intercept_filepos = 0;
+ intercept_delay_ms = 0;
+}
+
+bool intercept_triggered()
+{
+ return intercept_count == 0;
+}
+
+void intercept_setup_error(const char *filename, unsigned int errorafter, int errortoreturn, int syscalltoerror)
+{
+ TRACE4("Setup for error: %s, after %d, err %d, syscall %d\n", filename, errorafter, errortoreturn, syscalltoerror);
+ intercept_count = 1;
+ intercept_filename = filename;
+ intercept_filedes = -1;
+ intercept_errorafter = errorafter;
+ intercept_syscall = syscalltoerror;
+ intercept_errno = errortoreturn;
+ intercept_filepos = 0;
+ intercept_delay_ms = 0;
+}
+
+void intercept_setup_delay(const char *filename, unsigned int delay_after,
+ int delay_ms, int syscall_to_delay, int num_delays)
+{
+ TRACE5("Setup for delay: %s, after %d, wait %d ms, times %d, "
+ "syscall %d\n", filename, delay_after, delay_ms,
+ num_delays, syscall_to_delay);
+ intercept_count = num_delays;
+ intercept_filename = filename;
+ intercept_filedes = -1;
+ intercept_errorafter = delay_after;
+ intercept_syscall = syscall_to_delay;
+ intercept_errno = 0;
+ intercept_filepos = 0;
+ intercept_delay_ms = delay_ms;
+}
+bool intercept_errornow(int d, int size, int syscallnum)
+{
+ if(intercept_filedes != -1 && d == intercept_filedes && syscallnum == intercept_syscall)
+ {
+ //printf("Checking for err, %d, %d, %d\n", d, size, syscallnum);
+ if(size == SIZE_ALWAYS_ERROR)
+ {
+ // Looks good for an error!
+ TRACE2("Returning error %d for syscall %d\n", intercept_errno, syscallnum);
+ return true;
+ }
+ // where are we in the file?
+ if(intercept_filepos >= intercept_errorafter || intercept_filepos >= ((off_t)intercept_errorafter - size))
+ {
+ if (intercept_errno != 0)
+ {
+ TRACE3("Returning error %d for syscall %d, "
+ "file pos %d\n", intercept_errno,
+ syscallnum, (int)intercept_filepos);
+ }
+ else if (intercept_delay_ms != 0)
+ {
+ TRACE3("Delaying %d ms for syscall %d, "
+ "file pos %d\n", intercept_delay_ms,
+ syscallnum, (int)intercept_filepos);
+ }
+
+ return true;
+ }
+ }
+ return false; // no error please!
+}
+
+int intercept_reterr()
+{
+ int err = intercept_errno;
+ intercept_clear_setup();
+ return err;
+}
+
+#define CHECK_FOR_FAKE_ERROR_COND(D, S, CALL, FAILRES) \
+ if(intercept_count > 0) \
+ { \
+ if(intercept_errornow(D, S, CALL)) \
+ { \
+ if(intercept_delay_ms > 0) \
+ { \
+ struct timespec tm; \
+ tm.tv_sec = intercept_delay_ms / 1000; \
+ tm.tv_nsec = (intercept_delay_ms % 1000) \
+ * 1000000; \
+ while (nanosleep(&tm, &tm) != 0 && \
+ errno == EINTR) { } \
+ intercept_count --; \
+ if (intercept_count == 0) \
+ { \
+ intercept_clear_setup(); \
+ } \
+ } \
+ else \
+ { \
+ errno = intercept_reterr(); \
+ return FAILRES; \
+ } \
+ } \
+ }
+
+extern "C" int
+open(const char *path, int flags, mode_t mode)
+{
+ if(intercept_count > 0)
+ {
+ if(intercept_syscall == SYS_open && strcmp(path, intercept_filename) == 0)
+ {
+ errno = intercept_reterr();
+ return -1;
+ }
+ }
+#ifdef PLATFORM_NO_SYSCALL
+ int r = TEST_open(path, flags, mode);
+#else
+ int r = syscall(SYS_open, path, flags, mode);
+#endif
+ if(intercept_count > 0 && intercept_filedes == -1)
+ {
+ // Right file?
+ if(strcmp(intercept_filename, path) == 0)
+ {
+ intercept_filedes = r;
+ //printf("Found file to intercept, h = %d\n", r);
+ }
+ }
+ return r;
+}
+
+extern "C" int
+open64(const char *path, int flags, mode_t mode)
+{
+ // With _FILE_OFFSET_BITS set to 64 this should really use (flags |
+ // O_LARGEFILE) here, but not actually necessary for the tests and not
+ // worth the trouble finding O_LARGEFILE
+ return open(path, flags, mode);
+}
+
+extern "C" int
+close(int d)
+{
+ CHECK_FOR_FAKE_ERROR_COND(d, SIZE_ALWAYS_ERROR, SYS_close, -1);
+#ifdef PLATFORM_NO_SYSCALL
+ int r = TEST_close(d);
+#else
+ int r = syscall(SYS_close, d);
+#endif
+ if(r == 0)
+ {
+ if(d == intercept_filedes)
+ {
+ intercept_filedes = -1;
+ }
+ }
+ return r;
+}
+
+extern "C" ssize_t
+write(int d, const void *buf, size_t nbytes)
+{
+ CHECK_FOR_FAKE_ERROR_COND(d, nbytes, SYS_write, -1);
+#ifdef PLATFORM_NO_SYSCALL
+ int r = TEST_write(d, buf, nbytes);
+#else
+ int r = syscall(SYS_write, d, buf, nbytes);
+#endif
+ if(r != -1)
+ {
+ intercept_filepos += r;
+ }
+ return r;
+}
+
+extern "C" ssize_t
+read(int d, void *buf, size_t nbytes)
+{
+ CHECK_FOR_FAKE_ERROR_COND(d, nbytes, SYS_read, -1);
+#ifdef PLATFORM_NO_SYSCALL
+ int r = TEST_read(d, buf, nbytes);
+#else
+ int r = syscall(SYS_read, d, buf, nbytes);
+#endif
+ if(r != -1)
+ {
+ intercept_filepos += r;
+ }
+ return r;
+}
+
+extern "C" ssize_t
+readv(int d, const struct iovec *iov, int iovcnt)
+{
+ // how many bytes?
+ int nbytes = 0;
+ for(int b = 0; b < iovcnt; ++b)
+ {
+ nbytes += iov[b].iov_len;
+ }
+
+ CHECK_FOR_FAKE_ERROR_COND(d, nbytes, SYS_readv, -1);
+#ifdef PLATFORM_NO_SYSCALL
+ int r = TEST_readv(d, iov, iovcnt);
+#else
+ int r = syscall(SYS_readv, d, iov, iovcnt);
+#endif
+ if(r != -1)
+ {
+ intercept_filepos += r;
+ }
+ return r;
+}
+
+extern "C" off_t
+lseek(int fildes, off_t offset, int whence)
+{
+ // random magic for lseek syscall, see /usr/src/lib/libc/sys/lseek.c
+ CHECK_FOR_FAKE_ERROR_COND(fildes, 0, SYS_lseek, -1);
+#ifdef PLATFORM_NO_SYSCALL
+ int r = TEST_lseek(fildes, offset, whence);
+#else
+ #ifdef HAVE_LSEEK_DUMMY_PARAM
+ off_t r = syscall(SYS_lseek, fildes, 0 /* extra 0 required here! */, offset, whence);
+ #elif defined(_FILE_OFFSET_BITS)
+ // Don't bother trying to call SYS__llseek on 32 bit since it is
+ // fiddly and not needed for the tests
+ off_t r = syscall(SYS_lseek, fildes, (uint32_t)offset, whence);
+ #else
+ off_t r = syscall(SYS_lseek, fildes, offset, whence);
+ #endif
+#endif
+ if(r != -1)
+ {
+ intercept_filepos = r;
+ }
+ return r;
+}
+
+static opendir_t* opendir_real = NULL;
+static readdir_t* readdir_real = NULL;
+static readdir_t* readdir_hook = NULL;
+static closedir_t* closedir_real = NULL;
+static lstat_t* lstat_real = NULL;
+static lstat_t* lstat_hook = NULL;
+static const char* lstat_file = NULL;
+
+void intercept_setup_readdir_hook(const char *dirname, readdir_t hookfn)
+{
+ if (hookfn != NULL && dirname == NULL)
+ {
+ dirname = intercept_filename;
+ }
+
+ if (hookfn != NULL)
+ {
+ TRACE2("readdir hooked to %p for %s\n", hookfn, dirname);
+ }
+ else
+ {
+ TRACE2("readdir unhooked from %p for %s\n", readdir_hook,
+ intercept_filename);
+ }
+
+ intercept_filename = dirname;
+ readdir_hook = hookfn;
+}
+
+void intercept_setup_lstat_hook(const char *filename, lstat_t hookfn)
+{
+ /*
+ if (hookfn != NULL)
+ {
+ TRACE2("lstat hooked to %p for %s\n", hookfn, filename);
+ }
+ else
+ {
+ TRACE2("lstat unhooked from %p for %s\n", lstat_hook,
+ lstat_file);
+ }
+ */
+
+ lstat_file = filename;
+ lstat_hook = hookfn;
+}
+
+extern "C"
+DIR *opendir(const char *dirname)
+{
+ if (opendir_real == NULL)
+ {
+ opendir_real = (opendir_t*)(dlsym(RTLD_NEXT, "opendir"));
+ }
+
+ if (opendir_real == NULL)
+ {
+ perror("cannot find real opendir");
+ return NULL;
+ }
+
+ DIR* r = opendir_real(dirname);
+
+ if(readdir_hook != NULL && intercept_filedes == -1 &&
+ strcmp(intercept_filename, dirname) == 0)
+ {
+ intercept_filedes = dirfd(r);
+ //printf("Found file to intercept, h = %d\n", r);
+ }
+
+ return r;
+}
+
+extern "C"
+struct dirent *readdir(DIR *dir)
+{
+ if (readdir_hook != NULL && dirfd(dir) == intercept_filedes)
+ {
+ return readdir_hook(dir);
+ }
+
+ if (readdir_real == NULL)
+ {
+ #if readdir == readdir64
+ readdir_real = (readdir_t*)(dlsym(RTLD_NEXT, "readdir64"));
+ #else
+ readdir_real = (readdir_t*)(dlsym(RTLD_NEXT, "readdir"));
+ #endif
+ }
+
+ if (readdir_real == NULL)
+ {
+ perror("cannot find real readdir");
+ return NULL;
+ }
+
+ return readdir_real(dir);
+}
+
+extern "C"
+int closedir(DIR *dir)
+{
+ if (dirfd(dir) == intercept_filedes)
+ {
+ intercept_filedes = -1;
+ }
+
+ if (closedir_real == NULL)
+ {
+ closedir_real = (closedir_t*)(dlsym(RTLD_NEXT, "closedir"));
+ }
+
+ if (closedir_real == NULL)
+ {
+ perror("cannot find real closedir");
+ errno = ENOSYS;
+ return -1;
+ }
+
+ return closedir_real(dir);
+}
+
+extern "C" int
+#ifdef LINUX_WEIRD_LSTAT
+__lxstat(int ver, const char *file_name, STAT_STRUCT *buf)
+#else
+lstat(const char *file_name, STAT_STRUCT *buf)
+#endif
+{
+ if (lstat_real == NULL)
+ {
+ #ifdef LINUX_WEIRD_LSTAT
+ #if __lxstat == __lxstat64
+ lstat_real = (lstat_t*)(dlsym(RTLD_NEXT, "__lxstat64"));
+ #else
+ lstat_real = (lstat_t*)(dlsym(RTLD_NEXT, "__lxstat"));
+ #endif
+ #else
+ #if lstat == lstat64
+ lstat_real = (lstat_t*)(dlsym(RTLD_NEXT, "lstat64"));
+ #else
+ lstat_real = (lstat_t*)(dlsym(RTLD_NEXT, "lstat"));
+ #endif
+ #endif
+ }
+
+ if (lstat_real == NULL)
+ {
+ perror("cannot find real lstat");
+ errno = ENOSYS;
+ return -1;
+ }
+
+ if (lstat_hook == NULL || strcmp(file_name, lstat_file) != 0)
+ {
+ #ifdef LINUX_WEIRD_LSTAT
+ return lstat_real(ver, file_name, buf);
+ #else
+ return lstat_real(file_name, buf);
+ #endif
+ }
+
+ #ifdef LINUX_WEIRD_LSTAT
+ return lstat_hook(ver, file_name, buf);
+ #else
+ return lstat_hook(file_name, buf);
+ #endif
+}
+
+#endif // n PLATFORM_CLIB_FNS_INTERCEPTION_IMPOSSIBLE
diff --git a/lib/intercept/intercept.h b/lib/intercept/intercept.h
new file mode 100644
index 00000000..bc6557f3
--- /dev/null
+++ b/lib/intercept/intercept.h
@@ -0,0 +1,47 @@
+// --------------------------------------------------------------------------
+//
+// File
+// Name: intercept.h
+// Purpose: Syscall interception code for unit tests
+// Created: 2006/11/29
+//
+// --------------------------------------------------------------------------
+
+#ifndef INTERCEPT_H
+#define INTERCEPT_H
+#ifndef PLATFORM_CLIB_FNS_INTERCEPTION_IMPOSSIBLE
+
+#include <dirent.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+extern "C"
+{
+ typedef DIR *(opendir_t) (const char *name);
+ typedef struct dirent *(readdir_t) (DIR *dir);
+ typedef struct dirent *(readdir_t) (DIR *dir);
+ typedef int (closedir_t)(DIR *dir);
+#if defined __GNUC__ && __GNUC__ >= 2
+#define LINUX_WEIRD_LSTAT
+#define STAT_STRUCT struct stat /* should be stat64 */
+ typedef int (lstat_t) (int ver, const char *file_name,
+ STAT_STRUCT *buf);
+#else
+#define STAT_STRUCT struct stat
+ typedef int (lstat_t) (const char *file_name,
+ STAT_STRUCT *buf);
+#endif
+}
+
+void intercept_setup_error(const char *filename, unsigned int errorafter,
+ int errortoreturn, int syscalltoerror);
+void intercept_setup_delay(const char *filename, unsigned int delay_after,
+ int delay_ms, int syscall_to_delay, int num_delays);
+bool intercept_triggered();
+
+void intercept_setup_readdir_hook(const char *dirname, readdir_t hookfn);
+void intercept_setup_lstat_hook (const char *filename, lstat_t hookfn);
+
+#endif // !PLATFORM_CLIB_FNS_INTERCEPTION_IMPOSSIBLE
+#endif // !INTERCEPT_H
diff --git a/lib/raidfile/RaidFileController.cpp b/lib/raidfile/RaidFileController.cpp
index 89ae6791..0cc2ede7 100644
--- a/lib/raidfile/RaidFileController.cpp
+++ b/lib/raidfile/RaidFileController.cpp
@@ -59,13 +59,15 @@ RaidFileController::RaidFileController(const RaidFileController &rController)
// --------------------------------------------------------------------------
//
// Function
-// Name: RaidFileController::Initialise(const char *)
+// Name: RaidFileController::Initialise(const std::string&)
// Purpose: Initialises the system, loading the configuration file.
// Created: 2003/07/08
//
// --------------------------------------------------------------------------
-void RaidFileController::Initialise(const char *ConfigFilename)
+void RaidFileController::Initialise(const std::string& rConfigFilename)
{
+ MEMLEAKFINDER_NO_LEAKS;
+
static const ConfigurationVerifyKey verifykeys[] =
{
{"SetNumber", 0, ConfigTest_Exists | ConfigTest_IsInt, 0},
@@ -95,11 +97,12 @@ void RaidFileController::Initialise(const char *ConfigFilename)
// Load the configuration
std::string err;
- std::auto_ptr<Configuration> pconfig = Configuration::LoadAndVerify(ConfigFilename, &verify, err);
+ std::auto_ptr<Configuration> pconfig = Configuration::LoadAndVerify(
+ rConfigFilename, &verify, err);
if(pconfig.get() == 0 || !err.empty())
{
- fprintf(stderr, "RaidFile configuation file errors:\n%s", err.c_str());
+ BOX_ERROR("RaidFile configuration file errors: " << err);
THROW_EXCEPTION(RaidFileException, BadConfigFile)
}
diff --git a/lib/raidfile/RaidFileController.h b/lib/raidfile/RaidFileController.h
index 4962d236..783cb055 100644
--- a/lib/raidfile/RaidFileController.h
+++ b/lib/raidfile/RaidFileController.h
@@ -81,7 +81,8 @@ public:
~RaidFileController();
public:
- void Initialise(const char *ConfigFilename = "/etc/box/raidfile.conf");
+ void Initialise(const std::string& rConfigFilename =
+ "/etc/box/raidfile.conf");
int GetNumDiscSets() {return mSetList.size();}
// --------------------------------------------------------------------------
diff --git a/lib/raidfile/RaidFileException.txt b/lib/raidfile/RaidFileException.txt
index 6ad74563..c69dc2a2 100644
--- a/lib/raidfile/RaidFileException.txt
+++ b/lib/raidfile/RaidFileException.txt
@@ -11,7 +11,7 @@ NotOpen 7
OSError 8 Error when accessing an underlying file. Check file permissions allow files to be read and written in the configured raid directories.
WriteFileOpenOnTransform 9
WrongNumberOfDiscsInSet 10 There should be three directories in each disc set.
-RaidFileDoesntExist 11
+RaidFileDoesntExist 11 Error when accessing a file on the store. Check the store with bbstoreaccounts check.
ErrorOpeningFileForRead 12
FileIsDamagedNotRecoverable 13
InvalidRaidFile 14
diff --git a/lib/raidfile/RaidFileRead.cpp b/lib/raidfile/RaidFileRead.cpp
index ad040c22..187270f9 100644
--- a/lib/raidfile/RaidFileRead.cpp
+++ b/lib/raidfile/RaidFileRead.cpp
@@ -9,21 +9,18 @@
#include "Box.h"
-#include <sys/types.h>
-#include <unistd.h>
-#include <fcntl.h>
#include <errno.h>
-#include <sys/stat.h>
+#include <fcntl.h>
#include <stdarg.h>
+#include <unistd.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
#ifdef HAVE_SYS_UIO_H
#include <sys/uio.h>
#endif
-#ifdef HAVE_SYSLOG_H
- #include <syslog.h>
-#endif
-
#ifdef HAVE_DIRENT_H
#include <dirent.h>
#endif
@@ -41,7 +38,10 @@
#include "MemLeakFindOn.h"
#define READ_NUMBER_DISCS_REQUIRED 3
-#define READV_MAX_BLOCKS 64
+#define READV_MAX_BLOCKS 64
+
+// We want to use POSIX fstat() for now, not the emulated one
+#undef fstat
// --------------------------------------------------------------------------
//
@@ -544,8 +544,8 @@ void RaidFileRead_Raid::MoveDamagedFileAlertDaemon(int SetNumber, const std::str
// --------------------------------------------------------------------------
void RaidFileRead_Raid::AttemptToRecoverFromIOError(bool Stripe1)
{
- TRACE3("Attempting to recover from I/O error: %d %s, on stripe %d\n", mSetNumber, mFilename.c_str(), Stripe1?1:2);
- ::syslog(LOG_ERR | LOG_LOCAL5, "Attempting to recover from I/O error: %d %s, on stripe %d\n", mSetNumber, mFilename.c_str(), Stripe1?1:2);
+ BOX_WARNING("Attempting to recover from I/O error: " << mSetNumber <<
+ " " << mFilename << ", on stripe " << (Stripe1?1:2));
// Close offending file
if(Stripe1)
@@ -857,8 +857,10 @@ void RaidFileRead_Raid::SetPosition(pos_type FilePosition)
{
if(errno == EIO)
{
- TRACE3("I/O error when seeking in %d %s (to %d), stripe 1\n", mSetNumber, mFilename.c_str(), (int)FilePosition);
- ::syslog(LOG_ERR | LOG_LOCAL5, "I/O error when seeking in %d %s (to %d), stripe 1\n", mSetNumber, mFilename.c_str(), (int)FilePosition);
+ BOX_ERROR("I/O error when seeking in " <<
+ mSetNumber << " " << mFilename <<
+ " (to " << FilePosition << "), " <<
+ "stripe 1");
// Attempt to recover
AttemptToRecoverFromIOError(true /* is stripe 1 */);
ASSERT(mStripe1Handle == -1);
@@ -875,8 +877,10 @@ void RaidFileRead_Raid::SetPosition(pos_type FilePosition)
{
if(errno == EIO)
{
- TRACE3("I/O error when seeking in %d %s (to %d), stripe 2\n", mSetNumber, mFilename.c_str(), (int)FilePosition);
- ::syslog(LOG_ERR | LOG_LOCAL5, "I/O error when seeking in %d %s (to %d), stripe 2\n", mSetNumber, mFilename.c_str(), (int)FilePosition);
+ BOX_ERROR("I/O error when seeking in " <<
+ mSetNumber << " " << mFilename <<
+ " (to " << FilePosition << "), " <<
+ "stripe 2");
// Attempt to recover
AttemptToRecoverFromIOError(false /* is stripe 2 */);
ASSERT(mStripe2Handle == -1);
@@ -1050,8 +1054,9 @@ std::auto_ptr<RaidFileRead> RaidFileRead::Open(int SetNumber, const std::string
{
if(existance != RaidFileUtil::AsRaid)
{
- TRACE2("Opening %d %s in normal mode, but parity file doesn't exist\n", SetNumber, Filename.c_str());
- ::syslog(LOG_ERR | LOG_LOCAL5, "Opening %d %s in normal mode, but parity file doesn't exist\n", SetNumber, Filename.c_str());
+ BOX_ERROR("Opening " << SetNumber << " " <<
+ Filename << " in normal mode, but "
+ "parity file doesn't exist");
// TODO: Alert recovery daemon
}
@@ -1126,8 +1131,9 @@ std::auto_ptr<RaidFileRead> RaidFileRead::Open(int SetNumber, const std::string
bool oktotryagain = true;
if(stripe1errno == EIO)
{
- TRACE2("I/O error on opening %d %s stripe 1, trying recovery mode\n", SetNumber, Filename.c_str());
- ::syslog(LOG_ERR | LOG_LOCAL5, "I/O error on opening %d %s stripe 1, trying recovery mode\n", SetNumber, Filename.c_str());
+ BOX_ERROR("I/O error on opening " <<
+ SetNumber << " " << Filename <<
+ " stripe 1, trying recovery mode");
RaidFileRead_Raid::MoveDamagedFileAlertDaemon(SetNumber, Filename, true /* is stripe 1 */);
existingFiles = existingFiles & ~RaidFileUtil::Stripe1Exists;
@@ -1142,8 +1148,9 @@ std::auto_ptr<RaidFileRead> RaidFileRead::Open(int SetNumber, const std::string
if(stripe2errno == EIO)
{
- TRACE2("I/O error on opening %d %s stripe 2, trying recovery mode\n", SetNumber, Filename.c_str());
- ::syslog(LOG_ERR | LOG_LOCAL5, "I/O error on opening %d %s stripe 2, trying recovery mode\n", SetNumber, Filename.c_str());
+ BOX_ERROR("I/O error on opening " <<
+ SetNumber << " " << Filename <<
+ " stripe 2, trying recovery mode");
RaidFileRead_Raid::MoveDamagedFileAlertDaemon(SetNumber, Filename, false /* is stripe 2 */);
existingFiles = existingFiles & ~RaidFileUtil::Stripe2Exists;
@@ -1165,8 +1172,10 @@ std::auto_ptr<RaidFileRead> RaidFileRead::Open(int SetNumber, const std::string
if(existance == RaidFileUtil::AsRaidWithMissingReadable)
{
- TRACE3("Attempting to open RAID file %d %s in recovery mode (stripe %d present)\n", SetNumber, Filename.c_str(), (existingFiles & RaidFileUtil::Stripe1Exists)?1:2);
- ::syslog(LOG_ERR | LOG_LOCAL5, "Attempting to open RAID file %d %s in recovery mode (stripe %d present)\n", SetNumber, Filename.c_str(), (existingFiles & RaidFileUtil::Stripe1Exists)?1:2);
+ BOX_ERROR("Attempting to open RAID file " << SetNumber <<
+ " " << Filename << " in recovery mode (stripe " <<
+ ((existingFiles & RaidFileUtil::Stripe1Exists)?1:2) <<
+ " present)");
// Generate the filenames of all the lovely files
std::string stripe1Filename(RaidFileUtil::MakeRaidComponentName(rdiscSet, Filename, (0 + startDisc) % READ_NUMBER_DISCS_REQUIRED));
diff --git a/lib/raidfile/RaidFileWrite.cpp b/lib/raidfile/RaidFileWrite.cpp
index e30162fa..66ab81c8 100644
--- a/lib/raidfile/RaidFileWrite.cpp
+++ b/lib/raidfile/RaidFileWrite.cpp
@@ -35,6 +35,9 @@
// Must have this number of discs in the set
#define TRANSFORM_NUMBER_DISCS_REQUIRED 3
+// we want to use POSIX fstat() for now, not the emulated one
+#undef fstat
+
// --------------------------------------------------------------------------
//
// Function
@@ -104,7 +107,8 @@ void RaidFileWrite::Open(bool AllowOverwrite)
writeFilename += 'X';
// Attempt to open
- mOSFileHandle = ::open(writeFilename.c_str(), O_WRONLY | O_CREAT,
+ mOSFileHandle = ::open(writeFilename.c_str(),
+ O_WRONLY | O_CREAT | O_BINARY,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
if(mOSFileHandle == -1)
{
@@ -115,7 +119,7 @@ void RaidFileWrite::Open(bool AllowOverwrite)
#ifdef HAVE_FLOCK
int errnoBlock = EWOULDBLOCK;
if(::flock(mOSFileHandle, LOCK_EX | LOCK_NB) != 0)
-#else
+#elif HAVE_DECL_F_SETLK
int errnoBlock = EAGAIN;
struct flock desc;
desc.l_type = F_WRLCK;
@@ -123,6 +127,9 @@ void RaidFileWrite::Open(bool AllowOverwrite)
desc.l_start = 0;
desc.l_len = 0;
if(::fcntl(mOSFileHandle, F_SETLK, &desc) != 0)
+#else
+ int errnoBlock = ENOSYS;
+ if (0)
#endif
{
// Lock was not obtained.
@@ -242,23 +249,46 @@ void RaidFileWrite::Commit(bool ConvertToRaidNow)
}
// Rename it into place -- BEFORE it's closed so lock remains
+
+#ifdef WIN32
+ // Except on Win32 which doesn't allow renaming open files
+ // Close file...
+ if(::close(mOSFileHandle) != 0)
+ {
+ THROW_EXCEPTION(RaidFileException, OSError)
+ }
+ mOSFileHandle = -1;
+#endif // WIN32
+
RaidFileController &rcontroller(RaidFileController::GetController());
RaidFileDiscSet rdiscSet(rcontroller.GetDiscSet(mSetNumber));
// Get the filename for the write file
std::string renameTo(RaidFileUtil::MakeWriteFileName(rdiscSet, mFilename));
// And the current name
std::string renameFrom(renameTo + 'X');
+
+#ifdef WIN32
+ // need to delete the target first
+ if(::unlink(renameTo.c_str()) != 0 &&
+ GetLastError() != ERROR_FILE_NOT_FOUND)
+ {
+ THROW_EXCEPTION(RaidFileException, OSError)
+ }
+#endif
+
if(::rename(renameFrom.c_str(), renameTo.c_str()) != 0)
{
THROW_EXCEPTION(RaidFileException, OSError)
}
+#ifndef WIN32
// Close file...
if(::close(mOSFileHandle) != 0)
{
THROW_EXCEPTION(RaidFileException, OSError)
}
mOSFileHandle = -1;
+#endif // !WIN32
// Raid it?
if(ConvertToRaidNow)
@@ -292,8 +322,15 @@ void RaidFileWrite::Discard()
writeFilename += 'X';
// Unlink and close it
- if((::unlink(writeFilename.c_str()) != 0)
- || (::close(mOSFileHandle) != 0))
+
+#ifdef WIN32
+ // On Win32 we must close it first
+ if (::close(mOSFileHandle) != 0 ||
+ ::unlink(writeFilename.c_str()) != 0)
+#else // !WIN32
+ if (::unlink(writeFilename.c_str()) != 0 ||
+ ::close(mOSFileHandle) != 0)
+#endif // !WIN32
{
THROW_EXCEPTION(RaidFileException, OSError)
}
@@ -388,13 +425,13 @@ void RaidFileWrite::TransformToRaidStorage()
try
{
#if HAVE_DECL_O_EXLOCK
- FileHandleGuard<(O_WRONLY | O_CREAT | O_EXCL | O_EXLOCK)> stripe1(stripe1FilenameW.c_str());
- FileHandleGuard<(O_WRONLY | O_CREAT | O_EXCL | O_EXLOCK)> stripe2(stripe2FilenameW.c_str());
- FileHandleGuard<(O_WRONLY | O_CREAT | O_EXCL | O_EXLOCK)> parity(parityFilenameW.c_str());
+ FileHandleGuard<(O_WRONLY | O_CREAT | O_EXCL | O_EXLOCK | O_BINARY)> stripe1(stripe1FilenameW.c_str());
+ FileHandleGuard<(O_WRONLY | O_CREAT | O_EXCL | O_EXLOCK | O_BINARY)> stripe2(stripe2FilenameW.c_str());
+ FileHandleGuard<(O_WRONLY | O_CREAT | O_EXCL | O_EXLOCK | O_BINARY)> parity(parityFilenameW.c_str());
#else
- FileHandleGuard<(O_WRONLY | O_CREAT | O_EXCL)> stripe1(stripe1FilenameW.c_str());
- FileHandleGuard<(O_WRONLY | O_CREAT | O_EXCL)> stripe2(stripe2FilenameW.c_str());
- FileHandleGuard<(O_WRONLY | O_CREAT | O_EXCL)> parity(parityFilenameW.c_str());
+ FileHandleGuard<(O_WRONLY | O_CREAT | O_EXCL | O_BINARY)> stripe1(stripe1FilenameW.c_str());
+ FileHandleGuard<(O_WRONLY | O_CREAT | O_EXCL | O_BINARY)> stripe2(stripe2FilenameW.c_str());
+ FileHandleGuard<(O_WRONLY | O_CREAT | O_EXCL | O_BINARY)> parity(parityFilenameW.c_str());
#endif
// Then... read in data...
@@ -530,6 +567,21 @@ void RaidFileWrite::TransformToRaidStorage()
parity.Close();
stripe2.Close();
stripe1.Close();
+
+#ifdef WIN32
+ // Must delete before renaming
+ #define CHECK_UNLINK(file) \
+ { \
+ if (::unlink(file) != 0 && errno != ENOENT) \
+ { \
+ THROW_EXCEPTION(RaidFileException, OSError); \
+ } \
+ }
+ CHECK_UNLINK(stripe1Filename.c_str());
+ CHECK_UNLINK(stripe2Filename.c_str());
+ CHECK_UNLINK(parityFilename.c_str());
+ #undef CHECK_UNLINK
+#endif
// Rename them into place
if(::rename(stripe1FilenameW.c_str(), stripe1Filename.c_str()) != 0
diff --git a/lib/server/ConnectionException.txt b/lib/server/ConnectionException.txt
index 5056754f..c3429116 100644
--- a/lib/server/ConnectionException.txt
+++ b/lib/server/ConnectionException.txt
@@ -10,7 +10,7 @@ SocketConnectError 15 Probably a network issue between client and server, bad
TLSHandshakeFailed 30
TLSShutdownFailed 32
TLSWriteFailed 33 Probably a network issue between client and server.
-TLSReadFailed 34 Probably a network issue between client and server.
+TLSReadFailed 34 Probably a network issue between client and server, or a problem with the server.
TLSNoPeerCertificate 36
TLSPeerCertificateInvalid 37 Check certification process
TLSClosedWhenWriting 38
diff --git a/lib/server/Daemon.cpp b/lib/server/Daemon.cpp
index 2131bdcb..a9eb5bf5 100644
--- a/lib/server/Daemon.cpp
+++ b/lib/server/Daemon.cpp
@@ -19,10 +19,6 @@
#include <string.h>
#include <stdarg.h>
-#ifdef HAVE_SYSLOG_H
- #include <syslog.h>
-#endif
-
#ifdef WIN32
#include <ws2tcpip.h>
#endif
@@ -33,6 +29,7 @@
#include "Guards.h"
#include "UnixUser.h"
#include "FileModificationTime.h"
+#include "Logging.h"
#include "MemLeakFindOn.h"
@@ -48,11 +45,14 @@ Daemon *Daemon::spDaemon = 0;
//
// --------------------------------------------------------------------------
Daemon::Daemon()
- : mpConfiguration(0),
+ : mpConfiguration(NULL),
mReloadConfigWanted(false),
- mTerminateWanted(false)
+ mTerminateWanted(false),
+ mSingleProcess(false),
+ mRunInForeground(false),
+ mKeepConsoleOpenAfterFork(false)
{
- if(spDaemon != 0)
+ if(spDaemon != NULL)
{
THROW_EXCEPTION(ServerException, AlreadyDaemonConstructed)
}
@@ -79,56 +79,184 @@ Daemon::~Daemon()
delete mpConfiguration;
mpConfiguration = 0;
}
+
+ ASSERT(spDaemon == this);
+ spDaemon = NULL;
}
// --------------------------------------------------------------------------
//
// Function
// Name: Daemon::Main(const char *, int, const char *[])
-// Purpose: Starts the daemon off -- equivalent of C main() function
+// Purpose: Parses command-line options, and then calls
+// Main(std::string& configFile, bool singleProcess)
+// to start the daemon.
// Created: 2003/07/29
//
// --------------------------------------------------------------------------
int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
{
- // Banner (optional)
+ // Find filename of config file
+ mConfigFileName = DefaultConfigFile;
+ bool haveConfigFile = false;
+
+ #ifdef NDEBUG
+ int masterLevel = Log::NOTICE; // need an int to do math with
+ #else
+ int masterLevel = Log::INFO; // need an int to do math with
+ #endif
+
+ signed int c;
+
+ // reset getopt, just in case anybody used it before.
+ // unfortunately glibc and BSD differ on this point!
+ // http://www.ussg.iu.edu/hypermail/linux/kernel/0305.3/0262.html
+ #ifdef __GLIBC__
+ optind = 0;
+ #else
+ optind = 1;
+ optreset = 1;
+ #endif
+
+ while((c = getopt(argc, (char * const *)argv, "c:DFqvVt:Tk")) != -1)
{
- const char *banner = DaemonBanner();
- if(banner != 0)
+ switch(c)
{
- printf("%s", banner);
- }
- }
+ case 'c':
+ {
+ mConfigFileName = optarg;
+ haveConfigFile = true;
+ }
+ break;
- std::string pidFileName;
+ case 'D':
+ {
+ mSingleProcess = true;
+ }
+ break;
- try
- {
- // Find filename of config file
- mConfigFileName = DefaultConfigFile;
- if(argc >= 2)
- {
- // First argument is config file, or it's -c and the next arg is the config file
- if(::strcmp(argv[1], "-c") == 0 && argc >= 3)
+ case 'F':
{
- mConfigFileName = argv[2];
+ mRunInForeground = true;
}
- else
+ break;
+
+ case 'q':
{
- mConfigFileName = argv[1];
+ if(masterLevel == Log::NOTHING)
+ {
+ BOX_FATAL("Too many '-q': "
+ "Cannot reduce logging "
+ "level any more");
+ return 2;
+ }
+ masterLevel--;
}
- }
-
- // Test mode with no daemonisation?
- bool asDaemon = true;
- if(argc >= 3)
- {
- if(::strcmp(argv[2], "SINGLEPROCESS") == 0)
+ break;
+
+ case 'v':
+ {
+ if(masterLevel == Log::EVERYTHING)
+ {
+ BOX_FATAL("Too many '-v': "
+ "Cannot increase logging "
+ "level any more");
+ return 2;
+ }
+ masterLevel++;
+ }
+ break;
+
+ case 'V':
+ {
+ masterLevel = Log::EVERYTHING;
+ }
+ break;
+
+ case 't':
+ {
+ Console::SetTag(optarg);
+ }
+ break;
+
+ case 'T':
+ {
+ Console::SetShowTime(true);
+ }
+ break;
+
+ case 'k':
+ {
+ mKeepConsoleOpenAfterFork = true;
+ }
+ break;
+
+ case '?':
+ {
+ BOX_FATAL("Unknown option on command line: "
+ << "'" << (char)optopt << "'");
+ return 2;
+ }
+ break;
+
+ default:
{
- asDaemon = false;
+ BOX_FATAL("Unknown error in getopt: returned "
+ << "'" << c << "'");
+ return 1;
}
}
+ }
+
+ if (argc > optind && !haveConfigFile)
+ {
+ mConfigFileName = argv[optind]; optind++;
+ }
+
+ if (argc > optind && ::strcmp(argv[optind], "SINGLEPROCESS") == 0)
+ {
+ mSingleProcess = true; optind++;
+ }
+
+ if (argc > optind)
+ {
+ BOX_FATAL("Unknown parameter on command line: "
+ << "'" << std::string(argv[optind]) << "'");
+ return 2;
+ }
+
+ Logging::SetGlobalLevel((Log::Level)masterLevel);
+
+ return Main(mConfigFileName);
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: Daemon::Main(const std::string& rConfigFileName)
+// Purpose: Starts the daemon off -- equivalent of C main() function
+// Created: 2003/07/29
+//
+// --------------------------------------------------------------------------
+int Daemon::Main(const std::string &rConfigFileName)
+{
+ // Banner (optional)
+ {
+ const char *banner = DaemonBanner();
+ if(banner != 0)
+ {
+ BOX_NOTICE(banner);
+ }
+ }
+
+ std::string pidFileName;
+ mConfigFileName = rConfigFileName;
+
+ bool asDaemon = !mSingleProcess && !mRunInForeground;
+
+ try
+ {
// Load the configuration file.
std::string errors;
std::auto_ptr<Configuration> pconfig;
@@ -144,16 +272,9 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
if(e.GetType() == CommonException::ExceptionType &&
e.GetSubType() == CommonException::OSFileOpenError)
{
- fprintf(stderr, "%s: failed to start: "
- "failed to open configuration file: "
- "%s\n", DaemonName(),
- mConfigFileName.c_str());
-#ifdef WIN32
- ::syslog(LOG_ERR, "%s: failed to start: "
- "failed to open configuration file: "
- "%s", DaemonName(),
- mConfigFileName.c_str());
-#endif
+ BOX_FATAL("Failed to start: failed to open "
+ "configuration file: "
+ << mConfigFileName);
return 1;
}
@@ -164,14 +285,8 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
if(pconfig.get() == 0 || !errors.empty())
{
// Tell user about errors
- fprintf(stderr, "%s: Errors in config file %s:\n%s",
- DaemonName(), mConfigFileName.c_str(),
- errors.c_str());
-#ifdef WIN32
- ::syslog(LOG_ERR, "%s: Errors in config file %s:\n%s",
- DaemonName(), mConfigFileName.c_str(),
- errors.c_str());
-#endif
+ BOX_FATAL("Failed to start: errors in configuration "
+ "file: " << mConfigFileName << ": " << errors);
// And give up
return 1;
}
@@ -183,18 +298,6 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
// Let the derived class have a go at setting up stuff in the initial process
SetupInInitialProcess();
-#ifndef WIN32
- // Set signal handler
- struct sigaction sa;
- sa.sa_handler = SignalHandler;
- sa.sa_flags = 0;
- sigemptyset(&sa.sa_mask); // macro
- if(::sigaction(SIGHUP, &sa, NULL) != 0 || ::sigaction(SIGTERM, &sa, NULL) != 0)
- {
- THROW_EXCEPTION(ServerException, DaemoniseFailed)
- }
-#endif // !WIN32
-
// Server configuration
const Configuration &serverConfig(
mpConfiguration->GetSubConfiguration("Server"));
@@ -232,7 +335,7 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
default:
// parent
- _exit(0);
+ // _exit(0);
return 0;
break;
@@ -246,7 +349,8 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
// Set new session
if(::setsid() == -1)
{
- ::syslog(LOG_ERR, "can't setsid");
+ BOX_ERROR("Failed to setsid(): " <<
+ strerror(errno));
THROW_EXCEPTION(ServerException, DaemoniseFailed)
}
@@ -269,14 +373,24 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
break;
}
}
-#endif // ! WIN32
- // open the log
- ::openlog(DaemonName(), LOG_PID, LOG_LOCAL6);
+ // Set signal handler
+ // Don't do this in the parent, since it might be anything
+ // (e.g. test/bbackupd)
+
+ struct sigaction sa;
+ sa.sa_handler = SignalHandler;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask); // macro
+ if(::sigaction(SIGHUP, &sa, NULL) != 0 || ::sigaction(SIGTERM, &sa, NULL) != 0)
+ {
+ THROW_EXCEPTION(ServerException, DaemoniseFailed)
+ }
+#endif // !WIN32
// Log the start message
- ::syslog(LOG_INFO, "Starting daemon (config: %s) (version "
- BOX_VERSION ")", mConfigFileName.c_str());
+ BOX_NOTICE("Starting daemon, version " << BOX_VERSION
+ << ", config: " << mConfigFileName);
// Write PID to file
char pid[32];
@@ -289,7 +403,7 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
if(::write(pidFile, pid, pidsize) != pidsize)
{
- ::syslog(LOG_ERR, "can't write pid file");
+ BOX_FATAL("can't write pid file");
THROW_EXCEPTION(ServerException, DaemoniseFailed)
}
@@ -302,7 +416,7 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
}
#endif // BOX_MEMORY_LEAK_TESTING
- if(asDaemon)
+ if(asDaemon && !mKeepConsoleOpenAfterFork)
{
#ifndef WIN32
// Close standard streams
@@ -330,37 +444,24 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
// And definitely don't try and send anything to those file descriptors
// -- this has in the past sent text to something which isn't expecting it.
TRACE_TO_STDOUT(false);
+ Logging::ToConsole(false);
}
}
catch(BoxException &e)
{
- fprintf(stderr, "%s: failed to start: exception %s (%d/%d)\n",
- DaemonName(), e.what(), e.GetType(), e.GetSubType());
-#ifdef WIN32
- ::syslog(LOG_ERR, "%s: failed to start: "
- "exception %s (%d/%d)\n", DaemonName(),
- e.what(), e.GetType(), e.GetSubType());
-#endif
+ BOX_FATAL("Failed to start: exception " << e.what()
+ << " (" << e.GetType()
+ << "/" << e.GetSubType() << ")");
return 1;
}
catch(std::exception &e)
{
- fprintf(stderr, "%s: failed to start: exception %s\n",
- DaemonName(), e.what());
-#ifdef WIN32
- ::syslog(LOG_ERR, "%s: failed to start: exception %s\n",
- DaemonName(), e.what());
-#endif
+ BOX_FATAL("Failed to start: exception " << e.what());
return 1;
}
catch(...)
{
- fprintf(stderr, "%s: failed to start: unknown exception\n",
- DaemonName());
-#ifdef WIN32
- ::syslog(LOG_ERR, "%s: failed to start: unknown exception\n",
- DaemonName());
-#endif
+ BOX_FATAL("Failed to start: unknown error");
return 1;
}
@@ -373,7 +474,7 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
if (WSAStartup(0x0101, &info) == SOCKET_ERROR)
{
// will not run without sockets
- ::syslog(LOG_ERR, "Failed to initialise Windows Sockets");
+ BOX_FATAL("Failed to initialise Windows Sockets");
THROW_EXCEPTION(CommonException, Internal)
}
#endif
@@ -390,9 +491,8 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
if(mReloadConfigWanted && !mTerminateWanted)
{
// Need to reload that config file...
- ::syslog(LOG_INFO, "Reloading configuration "
- "(config: %s)",
- mConfigFileName.c_str());
+ BOX_NOTICE("Reloading configuration file: "
+ << mConfigFileName);
std::string errors;
std::auto_ptr<Configuration> pconfig =
Configuration::LoadAndVerify(
@@ -403,10 +503,9 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
if(pconfig.get() == 0 || !errors.empty())
{
// Tell user about errors
- ::syslog(LOG_ERR, "Errors in config "
- "file %s:\n%s",
- mConfigFileName.c_str(),
- errors.c_str());
+ BOX_FATAL("Error in configuration "
+ << "file: " << mConfigFileName
+ << ": " << errors);
// And give up
retcode = 1;
break;
@@ -430,25 +529,23 @@ int Daemon::Main(const char *DefaultConfigFile, int argc, const char *argv[])
::unlink(pidFileName.c_str());
// Log
- ::syslog(LOG_INFO, "Terminating daemon");
+ BOX_NOTICE("Terminating daemon");
}
catch(BoxException &e)
{
- ::syslog(LOG_ERR, "%s: terminating due to exception %s "
- "(%d/%d)", DaemonName(), e.what(), e.GetType(),
- e.GetSubType());
+ BOX_FATAL("Terminating due to exception " << e.what()
+ << " (" << e.GetType()
+ << "/" << e.GetSubType() << ")");
retcode = 1;
}
catch(std::exception &e)
{
- ::syslog(LOG_ERR, "%s: terminating due to exception %s",
- DaemonName(), e.what());
+ BOX_FATAL("Terminating due to exception " << e.what());
retcode = 1;
}
catch(...)
{
- ::syslog(LOG_ERR, "%s: terminating due to unknown exception",
- DaemonName());
+ BOX_FATAL("Terminating due to unknown exception");
retcode = 1;
}
diff --git a/lib/server/Daemon.h b/lib/server/Daemon.h
index dbc83e98..bce6c3c1 100644
--- a/lib/server/Daemon.h
+++ b/lib/server/Daemon.h
@@ -41,6 +41,7 @@ private:
public:
int Main(const char *DefaultConfigFile, int argc, const char *argv[]);
+ int Main(const std::string &rConfigFile);
virtual void Run();
const Configuration &GetConfiguration() const;
@@ -65,6 +66,7 @@ public:
protected:
box_time_t GetLoadedConfigModifiedTime() const;
+ bool IsSingleProcess() { return mSingleProcess; }
private:
static void SignalHandler(int sigraised);
@@ -76,6 +78,9 @@ private:
box_time_t mLoadedConfigModifiedTime;
bool mReloadConfigWanted;
bool mTerminateWanted;
+ bool mSingleProcess;
+ bool mRunInForeground;
+ bool mKeepConsoleOpenAfterFork;
static Daemon *spDaemon;
};
diff --git a/lib/server/LocalProcessStream.cpp b/lib/server/LocalProcessStream.cpp
index f1b6b2b8..af24de1b 100644
--- a/lib/server/LocalProcessStream.cpp
+++ b/lib/server/LocalProcessStream.cpp
@@ -18,10 +18,15 @@
#endif
#include "LocalProcessStream.h"
-#include "SocketStream.h"
#include "autogen_ServerException.h"
#include "Utils.h"
+#ifdef WIN32
+ #include "FileStream.h"
+#else
+ #include "SocketStream.h"
+#endif
+
#include "MemLeakFindOn.h"
#define MAX_ARGUMENTS 64
@@ -30,19 +35,22 @@
//
// Function
// Name: LocalProcessStream(const char *, pid_t &)
-// Purpose: Run a new process, and return a stream giving access to it's
-// stdin and stdout. Returns the PID of the new process -- this
-// must be waited on at some point to avoid zombies.
+// Purpose: Run a new process, and return a stream giving access
+// to its stdin and stdout (stdout and stderr on
+// Win32). Returns the PID of the new process -- this
+// must be waited on at some point to avoid zombies
+// (except on Win32).
// Created: 12/3/04
//
// --------------------------------------------------------------------------
std::auto_ptr<IOStream> LocalProcessStream(const char *CommandLine, pid_t &rPidOut)
{
+#ifndef WIN32
+
// Split up command
std::vector<std::string> command;
SplitString(std::string(CommandLine), ' ', command);
-#ifndef WIN32
// Build arguments
char *args[MAX_ARGUMENTS + 4];
{
@@ -101,10 +109,68 @@ std::auto_ptr<IOStream> LocalProcessStream(const char *CommandLine, pid_t &rPidO
// Return the stream object and PID
rPidOut = pid;
return stream;
+
#else // WIN32
- ::syslog(LOG_ERR, "vfork not implemented - LocalProcessStream.cpp");
- std::auto_ptr<IOStream> stream;
+
+ SECURITY_ATTRIBUTES secAttr;
+ secAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ secAttr.bInheritHandle = TRUE;
+ secAttr.lpSecurityDescriptor = NULL;
+
+ HANDLE writeInChild, readFromChild;
+ if(!CreatePipe(&readFromChild, &writeInChild, &secAttr, 0))
+ {
+ BOX_ERROR("Failed to CreatePipe for child process: " <<
+ GetErrorMessage(GetLastError()));
+ THROW_EXCEPTION(ServerException, SocketPairFailed)
+ }
+ SetHandleInformation(readFromChild, HANDLE_FLAG_INHERIT, 0);
+
+ PROCESS_INFORMATION procInfo;
+ STARTUPINFO startupInfo;
+
+ ZeroMemory(&procInfo, sizeof(procInfo));
+ ZeroMemory(&startupInfo, sizeof(startupInfo));
+ startupInfo.cb = sizeof(startupInfo);
+ startupInfo.hStdError = writeInChild;
+ startupInfo.hStdOutput = writeInChild;
+ startupInfo.hStdInput = INVALID_HANDLE_VALUE;
+ startupInfo.dwFlags |= STARTF_USESTDHANDLES;
+
+ CHAR* commandLineCopy = (CHAR*)malloc(strlen(CommandLine) + 1);
+ strcpy(commandLineCopy, CommandLine);
+
+ BOOL result = CreateProcess(NULL,
+ commandLineCopy, // command line
+ NULL, // process security attributes
+ NULL, // primary thread security attributes
+ TRUE, // handles are inherited
+ 0, // creation flags
+ NULL, // use parent's environment
+ NULL, // use parent's current directory
+ &startupInfo, // STARTUPINFO pointer
+ &procInfo); // receives PROCESS_INFORMATION
+
+ free(commandLineCopy);
+
+ if(!result)
+ {
+ BOX_ERROR("Failed to CreateProcess: '" << CommandLine <<
+ "': " << GetErrorMessage(GetLastError()));
+ CloseHandle(writeInChild);
+ CloseHandle(readFromChild);
+ THROW_EXCEPTION(ServerException, ServerForkError)
+ }
+
+ CloseHandle(procInfo.hProcess);
+ CloseHandle(procInfo.hThread);
+ CloseHandle(writeInChild);
+
+ rPidOut = (int)(procInfo.dwProcessId);
+
+ std::auto_ptr<IOStream> stream(new FileStream(readFromChild));
return stream;
+
#endif // ! WIN32
}
diff --git a/lib/server/Protocol.cpp b/lib/server/Protocol.cpp
index 988d44c8..4398a58f 100644
--- a/lib/server/Protocol.cpp
+++ b/lib/server/Protocol.cpp
@@ -22,6 +22,7 @@
#include "ServerException.h"
#include "PartialReadStream.h"
#include "ProtocolUncertainStream.h"
+#include "Logging.h"
#include "MemLeakFindOn.h"
@@ -55,7 +56,8 @@ Protocol::Protocol(IOStream &rStream)
mLastErrorType(NoError),
mLastErrorSubType(NoError)
{
- TRACE1("Send block allocation size is %d\n", PROTOCOL_ALLOCATE_SEND_BLOCK_CHUNK);
+ BOX_TRACE("Send block allocation size is " <<
+ PROTOCOL_ALLOCATE_SEND_BLOCK_CHUNK);
}
// --------------------------------------------------------------------------
diff --git a/lib/server/SSLLib.cpp b/lib/server/SSLLib.cpp
index 2a5bdbde..e9c990b9 100644
--- a/lib/server/SSLLib.cpp
+++ b/lib/server/SSLLib.cpp
@@ -14,10 +14,6 @@
#include <openssl/err.h>
#include <openssl/rand.h>
-#ifndef WIN32
-#include <syslog.h>
-#endif
-
#include "SSLLib.h"
#include "ServerException.h"
@@ -53,7 +49,8 @@ void SSLLib::Initialise()
THROW_EXCEPTION(ServerException, SSLRandomInitFailed)
}
#else
- ::fprintf(stderr, "No random device -- additional seeding of random number generator not performed.\n");
+ BOX_WARNING("No random device -- additional seeding of "
+ "random number generator not performed.");
#endif
}
@@ -73,13 +70,8 @@ void SSLLib::LogError(const char *ErrorDuringAction)
while((errcode = ERR_get_error()) != 0)
{
::ERR_error_string_n(errcode, errname, sizeof(errname));
- #ifndef NDEBUG
- if(SSLLib__TraceErrors)
- {
- TRACE2("SSL err during %s: %s\n", ErrorDuringAction, errname);
- }
- #endif
- ::syslog(LOG_ERR, "SSL err during %s: %s", ErrorDuringAction, errname);
+ BOX_ERROR("SSL error during " << ErrorDuringAction << ": " <<
+ errname);
}
}
diff --git a/lib/server/ServerControl.h b/lib/server/ServerControl.h
new file mode 100644
index 00000000..c51dd4e2
--- /dev/null
+++ b/lib/server/ServerControl.h
@@ -0,0 +1,178 @@
+#ifndef SERVER_CONTROL_H
+#define SERVER_CONTROL_H
+
+#include "Test.h"
+
+#ifdef WIN32
+
+#include "WinNamedPipeStream.h"
+#include "IOStreamGetLine.h"
+#include "BoxPortsAndFiles.h"
+
+static bool SendCommands(const std::string& rCmd)
+{
+ WinNamedPipeStream connection;
+
+ try
+ {
+ connection.Connect(BOX_NAMED_PIPE_NAME);
+ }
+ catch(...)
+ {
+ printf("Failed to connect to daemon control socket.\n");
+ return false;
+ }
+
+ // For receiving data
+ IOStreamGetLine getLine(connection);
+
+ // Wait for the configuration summary
+ std::string configSummary;
+ if(!getLine.GetLine(configSummary))
+ {
+ printf("Failed to receive configuration summary from daemon\n");
+ return false;
+ }
+
+ // Was the connection rejected by the server?
+ if(getLine.IsEOF())
+ {
+ printf("Server rejected the connection.\n");
+ return false;
+ }
+
+ // Decode it
+ int autoBackup, updateStoreInterval, minimumFileAge, maxUploadWait;
+ if(::sscanf(configSummary.c_str(), "bbackupd: %d %d %d %d",
+ &autoBackup, &updateStoreInterval,
+ &minimumFileAge, &maxUploadWait) != 4)
+ {
+ printf("Config summary didn't decode\n");
+ return false;
+ }
+
+ std::string cmds;
+ bool expectResponse;
+
+ if (rCmd != "")
+ {
+ cmds = rCmd;
+ cmds += "\nquit\n";
+ expectResponse = true;
+ }
+ else
+ {
+ cmds = "quit\n";
+ expectResponse = false;
+ }
+
+ connection.Write(cmds.c_str(), cmds.size());
+
+ // Read the response
+ std::string line;
+ bool statusOk = !expectResponse;
+
+ while (expectResponse && !getLine.IsEOF() && getLine.GetLine(line))
+ {
+ // Is this an OK or error line?
+ if (line == "ok")
+ {
+ statusOk = true;
+ }
+ else if (line == "error")
+ {
+ printf("ERROR (%s)\n", rCmd.c_str());
+ break;
+ }
+ else
+ {
+ printf("WARNING: Unexpected response to command '%s': "
+ "%s", rCmd.c_str(), line.c_str());
+ }
+ }
+
+ return statusOk;
+}
+
+inline bool HUPServer(int pid)
+{
+ return SendCommands("reload");
+}
+
+inline bool KillServerInternal(int pid)
+{
+ HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, false, pid);
+ if (hProcess == NULL)
+ {
+ printf("Failed to open process %d: error %d\n",
+ pid, (int)GetLastError());
+ return false;
+ }
+
+ if (!TerminateProcess(hProcess, 1))
+ {
+ printf("Failed to terminate process %d: error %d\n",
+ pid, (int)GetLastError());
+ CloseHandle(hProcess);
+ return false;
+ }
+
+ CloseHandle(hProcess);
+ return true;
+}
+
+#else // !WIN32
+
+inline bool HUPServer(int pid)
+{
+ if(pid == 0) return false;
+ return ::kill(pid, SIGHUP) == 0;
+}
+
+inline bool KillServerInternal(int pid)
+{
+ if(pid == 0 || pid == -1) return false;
+ bool killed = (::kill(pid, SIGTERM) == 0);
+ TEST_THAT(killed);
+ return killed;
+}
+
+#endif // WIN32
+
+inline bool KillServer(int pid)
+{
+ if (!KillServerInternal(pid))
+ {
+ return false;
+ }
+
+ for (int i = 0; i < 30; i++)
+ {
+ if (!ServerIsAlive(pid)) break;
+ ::sleep(1);
+ if (!ServerIsAlive(pid)) break;
+
+ if (i == 0)
+ {
+ printf("waiting for server to die");
+ }
+
+ printf(".");
+ fflush(stdout);
+ }
+
+ if (!ServerIsAlive(pid))
+ {
+ printf("done.\n");
+ }
+ else
+ {
+ printf("failed!\n");
+ }
+
+ fflush(stdout);
+
+ return !ServerIsAlive(pid);
+}
+
+#endif // SERVER_CONTROL_H
diff --git a/lib/server/ServerStream.h b/lib/server/ServerStream.h
index 998a266e..5f615336 100644
--- a/lib/server/ServerStream.h
+++ b/lib/server/ServerStream.h
@@ -14,7 +14,6 @@
#include <errno.h>
#ifndef WIN32
- #include <syslog.h>
#include <sys/wait.h>
#endif
@@ -73,7 +72,10 @@ public:
{
if(childExit)
{
- ::syslog(LOG_ERR, "in server child, exception %s (%d/%d) -- terminating child", e.what(), e.GetType(), e.GetSubType());
+ BOX_ERROR("Error in child process, "
+ "terminating connection: exception " <<
+ e.what() << "(" << e.GetType() <<
+ "/" << e.GetSubType() << ")");
_exit(1);
}
else throw;
@@ -82,7 +84,9 @@ public:
{
if(childExit)
{
- ::syslog(LOG_ERR, "in server child, exception %s -- terminating child", e.what());
+ BOX_ERROR("Error in child process, "
+ "terminating connection: exception " <<
+ e.what());
_exit(1);
}
else throw;
@@ -91,7 +95,9 @@ public:
{
if(childExit)
{
- ::syslog(LOG_ERR, "in server child, unknown exception -- terminating child");
+ BOX_ERROR("Error in child process, "
+ "terminating connection: "
+ "unknown exception");
_exit(1);
}
else throw;
@@ -170,16 +176,22 @@ public:
}
else if(c[0] == "unix")
{
- // Check arguments size
- if(c.size() != 2)
- {
- THROW_EXCEPTION(ServerException, ServerStreamBadListenAddrs)
- }
+ #ifdef WIN32
+ BOX_WARNING("Ignoring request to listen on a Unix socket on Windows: " << addrlist[a]);
+ delete psocket;
+ psocket = NULL;
+ #else
+ // Check arguments size
+ if(c.size() != 2)
+ {
+ THROW_EXCEPTION(ServerException, ServerStreamBadListenAddrs)
+ }
- // unlink anything there
- ::unlink(c[1].c_str());
-
- psocket->Listen(Socket::TypeUNIX, c[1].c_str());
+ // unlink anything there
+ ::unlink(c[1].c_str());
+
+ psocket->Listen(Socket::TypeUNIX, c[1].c_str());
+ #endif // WIN32
}
else
{
@@ -187,8 +199,11 @@ public:
THROW_EXCEPTION(ServerException, ServerStreamBadListenAddrs)
}
- // Add to list of sockets
- mSockets.push_back(psocket);
+ if (psocket != NULL)
+ {
+ // Add to list of sockets
+ mSockets.push_back(psocket);
+ }
}
catch(...)
{
@@ -196,8 +211,11 @@ public:
throw;
}
- // Add to the list of things to wait on
- connectionWait.Add(psocket);
+ if (psocket != NULL)
+ {
+ // Add to the list of things to wait on
+ connectionWait.Add(psocket);
+ }
}
}
@@ -217,7 +235,8 @@ public:
if(connection.get())
{
// Since this is a template parameter, the if() will be optimised out by the compiler
- if(WillForkToHandleRequests())
+ #ifndef WIN32 // no fork on Win32
+ if(ForkToHandleRequests && !IsSingleProcess())
{
pid_t pid = ::fork();
switch(pid)
@@ -253,22 +272,26 @@ public:
}
// Log it
- ::syslog(LOG_INFO, "%s (handling in child %d)", logMessage.c_str(), pid);
+ BOX_WARNING("Error message from child process " << pid << ": " << logMessage);
}
else
{
- // Just handle in this connection
+ #endif // !WIN32
+ // Just handle in this process
SetProcessTitle("handling");
HandleConnection(*connection);
SetProcessTitle("idle");
+ #ifndef WIN32
}
+ #endif // !WIN32
}
}
OnIdle();
+ #ifndef WIN32
// Clean up child processes (if forking daemon)
- if(WillForkToHandleRequests())
+ if(ForkToHandleRequests && !IsSingleProcess())
{
int status = 0;
int p = 0;
@@ -281,6 +304,7 @@ public:
}
} while(p > 0);
}
+ #endif // !WIN32
}
}
catch(...)
@@ -301,14 +325,14 @@ public:
virtual void Connection(StreamType &rStream) = 0;
protected:
- // For checking code in dervied classes -- use if you have an algorithm which
+ // For checking code in derived classes -- use if you have an algorithm which
// depends on the forking model in case someone changes it later.
bool WillForkToHandleRequests()
{
#ifdef WIN32
return false;
#else
- return ForkToHandleRequests;
+ return ForkToHandleRequests && !IsSingleProcess();
#endif // WIN32
}
diff --git a/lib/server/Socket.cpp b/lib/server/Socket.cpp
index afed4888..28dae69f 100644
--- a/lib/server/Socket.cpp
+++ b/lib/server/Socket.cpp
@@ -17,7 +17,6 @@
#ifndef WIN32
#include <sys/socket.h>
#include <netdb.h>
-#include <syslog.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
@@ -124,18 +123,20 @@ void Socket::LogIncomingConnection(const struct sockaddr *addr, socklen_t addrle
switch(addr->sa_family)
{
case AF_UNIX:
- ::syslog(LOG_INFO, "Incoming connection from local (UNIX socket)");
+ BOX_INFO("Incoming connection from local (UNIX socket)");
break;
case AF_INET:
{
sockaddr_in *a = (sockaddr_in*)addr;
- ::syslog(LOG_INFO, "Incoming connection from %s port %d", inet_ntoa(a->sin_addr), ntohs(a->sin_port));
+ BOX_INFO("Incoming connection from " <<
+ inet_ntoa(a->sin_addr) << " port " <<
+ ntohs(a->sin_port));
}
break;
default:
- ::syslog(LOG_INFO, "Incoming connection of unknown type");
+ BOX_WARNING("Incoming connection of unknown type");
break;
}
}
diff --git a/lib/server/SocketStream.cpp b/lib/server/SocketStream.cpp
index 61d3846f..ebc57041 100644
--- a/lib/server/SocketStream.cpp
+++ b/lib/server/SocketStream.cpp
@@ -15,6 +15,7 @@
#include <sys/types.h>
#include <errno.h>
+#include <string.h>
#ifndef WIN32
#include <poll.h>
@@ -164,6 +165,10 @@ void SocketStream::Open(int Type, const char *Name, int Port)
#else
::close(mSocketHandle);
#endif
+ BOX_ERROR("Failed to connect to socket (type " << Type <<
+ ", name " << Name << ", port " << Port << "): " <<
+ "error " << errno << " (" << strerror(errno) <<
+ ")");
mSocketHandle = INVALID_SOCKET_VALUE;
THROW_EXCEPTION(ConnectionException, Conn_SocketConnectError)
}
diff --git a/lib/server/SocketStream.h b/lib/server/SocketStream.h
index 7d5e6d93..51f2e306 100644
--- a/lib/server/SocketStream.h
+++ b/lib/server/SocketStream.h
@@ -14,7 +14,7 @@
#ifdef WIN32
typedef SOCKET tOSSocketHandle;
- #define INVALID_SOCKET_VALUE INVALID_SOCKET
+ #define INVALID_SOCKET_VALUE (tOSSocketHandle)(-1)
#else
typedef int tOSSocketHandle;
#define INVALID_SOCKET_VALUE -1
@@ -67,6 +67,7 @@ public:
off_t GetBytesRead() const {return mBytesRead;}
off_t GetBytesWritten() const {return mBytesWritten;}
void ResetCounters() {mBytesRead = mBytesWritten = 0;}
+ bool IsOpened() { return mSocketHandle != INVALID_SOCKET_VALUE; }
};
#endif // SOCKETSTREAM__H
diff --git a/lib/server/SocketStreamTLS.cpp b/lib/server/SocketStreamTLS.cpp
index af4ad460..58dc5754 100644
--- a/lib/server/SocketStreamTLS.cpp
+++ b/lib/server/SocketStreamTLS.cpp
@@ -23,6 +23,7 @@
#include "SSLLib.h"
#include "ServerException.h"
#include "TLSContext.h"
+#include "BoxTime.h"
#include "MemLeakFindOn.h"
@@ -244,20 +245,30 @@ bool SocketStreamTLS::WaitWhenRetryRequired(int SSLErrorCode, int Timeout)
break;
}
p.revents = 0;
- switch(::poll(&p, 1, (Timeout == IOStream::TimeOutInfinite)?INFTIM:Timeout))
+
+ int64_t start, end;
+ start = BoxTimeToMilliSeconds(GetCurrentBoxTime());
+ end = start + Timeout;
+ int result;
+
+ do
{
- case -1:
- // error
- if(errno == EINTR)
- {
- // Signal. Do "time out"
- return false;
- }
- else
+ int64_t now = BoxTimeToMilliSeconds(GetCurrentBoxTime());
+ int poll_timeout = (int)(end - now);
+ if (poll_timeout < 0) poll_timeout = 0;
+ if (Timeout == IOStream::TimeOutInfinite)
{
- // Bad!
- THROW_EXCEPTION(ServerException, SocketPollError)
+ poll_timeout = INFTIM;
}
+ result = ::poll(&p, 1, poll_timeout);
+ }
+ while(result == -1 && errno == EINTR);
+
+ switch(result)
+ {
+ case -1:
+ // error - Bad!
+ THROW_EXCEPTION(ServerException, SocketPollError)
break;
case 0:
diff --git a/lib/server/TLSContext.cpp b/lib/server/TLSContext.cpp
index cc125d00..49143801 100644
--- a/lib/server/TLSContext.cpp
+++ b/lib/server/TLSContext.cpp
@@ -61,6 +61,11 @@ TLSContext::~TLSContext()
// --------------------------------------------------------------------------
void TLSContext::Initialise(bool AsServer, const char *CertificatesFile, const char *PrivateKeyFile, const char *TrustedCAsFile)
{
+ if(mpContext != 0)
+ {
+ ::SSL_CTX_free(mpContext);
+ }
+
mpContext = ::SSL_CTX_new(AsServer?TLSv1_server_method():TLSv1_client_method());
if(mpContext == NULL)
{
diff --git a/lib/server/WinNamedPipeStream.cpp b/lib/server/WinNamedPipeStream.cpp
index c5b7eaa5..d7e90793 100644
--- a/lib/server/WinNamedPipeStream.cpp
+++ b/lib/server/WinNamedPipeStream.cpp
@@ -35,7 +35,9 @@
//
// --------------------------------------------------------------------------
WinNamedPipeStream::WinNamedPipeStream()
- : mSocketHandle(NULL),
+ : mSocketHandle(INVALID_HANDLE_VALUE),
+ mReadableEvent(INVALID_HANDLE_VALUE),
+ mBytesInBuffer(0),
mReadClosed(false),
mWriteClosed(false),
mIsServer(false),
@@ -53,9 +55,17 @@ WinNamedPipeStream::WinNamedPipeStream()
// --------------------------------------------------------------------------
WinNamedPipeStream::~WinNamedPipeStream()
{
- if (mSocketHandle != NULL)
+ if (mSocketHandle != INVALID_HANDLE_VALUE)
{
- Close();
+ try
+ {
+ Close();
+ }
+ catch (std::exception &e)
+ {
+ BOX_ERROR("Caught exception while destroying "
+ "named pipe, ignored: " << e.what());
+ }
}
}
@@ -70,16 +80,17 @@ WinNamedPipeStream::~WinNamedPipeStream()
// --------------------------------------------------------------------------
void WinNamedPipeStream::Accept(const wchar_t* pName)
{
- if (mSocketHandle != NULL || mIsConnected)
+ if (mSocketHandle != INVALID_HANDLE_VALUE || mIsConnected)
{
THROW_EXCEPTION(ServerException, SocketAlreadyOpen)
}
mSocketHandle = CreateNamedPipeW(
pName, // pipe name
- PIPE_ACCESS_DUPLEX, // read/write access
- PIPE_TYPE_MESSAGE | // message type pipe
- PIPE_READMODE_MESSAGE | // message-read mode
+ PIPE_ACCESS_DUPLEX | // read/write access
+ FILE_FLAG_OVERLAPPED, // enabled overlapped I/O
+ PIPE_TYPE_BYTE | // message type pipe
+ PIPE_READMODE_BYTE | // message-read mode
PIPE_WAIT, // blocking mode
1, // max. instances
4096, // output buffer size
@@ -87,10 +98,10 @@ void WinNamedPipeStream::Accept(const wchar_t* pName)
NMPWAIT_USE_DEFAULT_WAIT, // client time-out
NULL); // default security attribute
- if (mSocketHandle == NULL)
+ if (mSocketHandle == INVALID_HANDLE_VALUE)
{
- ::syslog(LOG_ERR, "CreateNamedPipeW failed: %d",
- GetLastError());
+ BOX_ERROR("Failed to CreateNamedPipeW(" << pName << "): " <<
+ GetErrorMessage(GetLastError()));
THROW_EXCEPTION(ServerException, SocketOpenError)
}
@@ -98,16 +109,48 @@ void WinNamedPipeStream::Accept(const wchar_t* pName)
if (!connected)
{
- ::syslog(LOG_ERR, "ConnectNamedPipe failed: %d",
- GetLastError());
+ BOX_ERROR("Failed to ConnectNamedPipe(" << pName << "): " <<
+ GetErrorMessage(GetLastError()));
Close();
THROW_EXCEPTION(ServerException, SocketOpenError)
}
+ mBytesInBuffer = 0;
mReadClosed = false;
mWriteClosed = false;
mIsServer = true; // must flush and disconnect before closing
mIsConnected = true;
+
+ // create the Readable event
+ mReadableEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ if (mReadableEvent == INVALID_HANDLE_VALUE)
+ {
+ BOX_ERROR("Failed to create the Readable event: " <<
+ GetErrorMessage(GetLastError()));
+ Close();
+ THROW_EXCEPTION(CommonException, Internal)
+ }
+
+ // initialise the OVERLAPPED structure
+ memset(&mReadOverlap, 0, sizeof(mReadOverlap));
+ mReadOverlap.hEvent = mReadableEvent;
+
+ // start the first overlapped read
+ if (!ReadFile(mSocketHandle, mReadBuffer, sizeof(mReadBuffer),
+ NULL, &mReadOverlap))
+ {
+ DWORD err = GetLastError();
+
+ if (err != ERROR_IO_PENDING)
+ {
+ BOX_ERROR("Failed to start overlapped read: " <<
+ GetErrorMessage(err));
+ Close();
+ THROW_EXCEPTION(ConnectionException,
+ Conn_SocketReadError)
+ }
+ }
}
// --------------------------------------------------------------------------
@@ -120,7 +163,7 @@ void WinNamedPipeStream::Accept(const wchar_t* pName)
// --------------------------------------------------------------------------
void WinNamedPipeStream::Connect(const wchar_t* pName)
{
- if (mSocketHandle != NULL || mIsConnected)
+ if (mSocketHandle != INVALID_HANDLE_VALUE || mIsConnected)
{
THROW_EXCEPTION(ServerException, SocketAlreadyOpen)
}
@@ -137,8 +180,17 @@ void WinNamedPipeStream::Connect(const wchar_t* pName)
if (mSocketHandle == INVALID_HANDLE_VALUE)
{
- ::syslog(LOG_ERR, "Failed to connect to server's named pipe: "
- "error %d", GetLastError());
+ DWORD err = GetLastError();
+ if (err == ERROR_PIPE_BUSY)
+ {
+ BOX_ERROR("Failed to connect to backup daemon: "
+ "it is busy with another connection");
+ }
+ else
+ {
+ BOX_ERROR("Failed to connect to backup daemon: " <<
+ GetErrorMessage(err));
+ }
THROW_EXCEPTION(ServerException, SocketOpenError)
}
@@ -159,33 +211,192 @@ void WinNamedPipeStream::Connect(const wchar_t* pName)
int WinNamedPipeStream::Read(void *pBuffer, int NBytes, int Timeout)
{
// TODO no support for timeouts yet
- ASSERT(Timeout == IOStream::TimeOutInfinite)
+ if (Timeout != IOStream::TimeOutInfinite)
+ {
+ THROW_EXCEPTION(CommonException, AssertFailed)
+ }
- if (mSocketHandle == NULL || !mIsConnected)
+ if (mSocketHandle == INVALID_HANDLE_VALUE || !mIsConnected)
{
THROW_EXCEPTION(ServerException, BadSocketHandle)
}
+ if (mReadClosed)
+ {
+ THROW_EXCEPTION(ConnectionException, SocketShutdownError)
+ }
+
+ // ensure safe to cast NBytes to unsigned
+ if (NBytes < 0)
+ {
+ THROW_EXCEPTION(CommonException, AssertFailed)
+ }
+
DWORD NumBytesRead;
-
- bool Success = ReadFile(
- mSocketHandle, // pipe handle
- pBuffer, // buffer to receive reply
- NBytes, // size of buffer
- &NumBytesRead, // number of bytes read
- NULL); // not overlapped
-
- if (!Success)
+
+ if (mIsServer)
{
- THROW_EXCEPTION(ConnectionException, Conn_SocketReadError)
+ // satisfy from buffer if possible, to avoid
+ // blocking on read.
+ bool needAnotherRead = false;
+ if (mBytesInBuffer == 0)
+ {
+ // overlapped I/O completed successfully?
+ // (wait if needed)
+
+ if (GetOverlappedResult(mSocketHandle,
+ &mReadOverlap, &NumBytesRead, TRUE))
+ {
+ needAnotherRead = true;
+ }
+ else
+ {
+ DWORD err = GetLastError();
+
+ if (err == ERROR_HANDLE_EOF)
+ {
+ mReadClosed = true;
+ }
+ else
+ {
+ if (err == ERROR_BROKEN_PIPE)
+ {
+ BOX_ERROR("Control client "
+ "disconnected");
+ }
+ else
+ {
+ BOX_ERROR("Failed to wait for "
+ "ReadFile to complete: "
+ << GetErrorMessage(err));
+ }
+
+ Close();
+ THROW_EXCEPTION(ConnectionException,
+ Conn_SocketReadError)
+ }
+ }
+ }
+ else
+ {
+ NumBytesRead = 0;
+ }
+
+ size_t BytesToCopy = NumBytesRead + mBytesInBuffer;
+ size_t BytesRemaining = 0;
+
+ if (BytesToCopy > (size_t)NBytes)
+ {
+ BytesRemaining = BytesToCopy - NBytes;
+ BytesToCopy = NBytes;
+ }
+
+ memcpy(pBuffer, mReadBuffer, BytesToCopy);
+ memmove(mReadBuffer, mReadBuffer + BytesToCopy, BytesRemaining);
+
+ mBytesInBuffer = BytesRemaining;
+ NumBytesRead = BytesToCopy;
+
+ if (needAnotherRead)
+ {
+ // reinitialise the OVERLAPPED structure
+ memset(&mReadOverlap, 0, sizeof(mReadOverlap));
+ mReadOverlap.hEvent = mReadableEvent;
+ }
+
+ // start the next overlapped read
+ if (needAnotherRead && !ReadFile(mSocketHandle,
+ mReadBuffer + mBytesInBuffer,
+ sizeof(mReadBuffer) - mBytesInBuffer,
+ NULL, &mReadOverlap))
+ {
+ DWORD err = GetLastError();
+ if (err == ERROR_IO_PENDING)
+ {
+ // Don't reset yet, there might be data
+ // in the buffer waiting to be read,
+ // will check below.
+ // ResetEvent(mReadableEvent);
+ }
+ else if (err == ERROR_HANDLE_EOF)
+ {
+ mReadClosed = true;
+ }
+ else if (err == ERROR_BROKEN_PIPE)
+ {
+ BOX_ERROR("Control client disconnected");
+ mReadClosed = true;
+ }
+ else
+ {
+ BOX_ERROR("Failed to start overlapped read: "
+ << GetErrorMessage(err));
+ Close();
+ THROW_EXCEPTION(ConnectionException,
+ Conn_SocketReadError)
+ }
+ }
+
+ // If the read succeeded immediately, leave the event
+ // signaled, so that we will be called again to process
+ // the newly read data and start another overlapped read.
+ if (needAnotherRead && !mReadClosed)
+ {
+ // leave signalled
+ }
+ else if (!needAnotherRead && mBytesInBuffer > 0)
+ {
+ // leave signalled
+ }
+ else
+ {
+ // nothing left to read, reset the event
+ ResetEvent(mReadableEvent);
+ // FIXME: a pending read could have signalled
+ // the event (again) while we were busy reading.
+ // that signal would be lost, and the reading
+ // thread would block. Should be pretty obvious
+ // if this happens in practice: control client
+ // hangs.
+ }
}
-
- // Closed for reading at EOF?
- if (NumBytesRead == 0)
+ else
{
- mReadClosed = true;
+ if (!ReadFile(
+ mSocketHandle, // pipe handle
+ pBuffer, // buffer to receive reply
+ NBytes, // size of buffer
+ &NumBytesRead, // number of bytes read
+ NULL)) // not overlapped
+ {
+ DWORD err = GetLastError();
+
+ Close();
+
+ // ERROR_NO_DATA is a strange name for
+ // "The pipe is being closed". No exception wanted.
+
+ if (err == ERROR_NO_DATA ||
+ err == ERROR_PIPE_NOT_CONNECTED)
+ {
+ NumBytesRead = 0;
+ }
+ else
+ {
+ BOX_ERROR("Failed to read from control socket: "
+ << GetErrorMessage(err));
+ THROW_EXCEPTION(ConnectionException,
+ Conn_SocketReadError)
+ }
+ }
+
+ // Closed for reading at EOF?
+ if (NumBytesRead == 0)
+ {
+ mReadClosed = true;
+ }
}
-
+
return NumBytesRead;
}
@@ -199,7 +410,7 @@ int WinNamedPipeStream::Read(void *pBuffer, int NBytes, int Timeout)
// --------------------------------------------------------------------------
void WinNamedPipeStream::Write(const void *pBuffer, int NBytes)
{
- if (mSocketHandle == NULL || !mIsConnected)
+ if (mSocketHandle == INVALID_HANDLE_VALUE || !mIsConnected)
{
THROW_EXCEPTION(ServerException, BadSocketHandle)
}
@@ -223,9 +434,23 @@ void WinNamedPipeStream::Write(const void *pBuffer, int NBytes)
if (!Success)
{
- mWriteClosed = true; // assume can't write again
- THROW_EXCEPTION(ConnectionException,
- Conn_SocketWriteError)
+ DWORD err = GetLastError();
+ BOX_ERROR("Failed to write to control socket: " <<
+ GetErrorMessage(err));
+ Close();
+
+ // ERROR_NO_DATA is a strange name for
+ // "The pipe is being closed". No exception wanted.
+
+ if (err == ERROR_NO_DATA)
+ {
+ return;
+ }
+ else
+ {
+ THROW_EXCEPTION(ConnectionException,
+ Conn_SocketWriteError)
+ }
}
NumBytesWrittenTotal += NumBytesWrittenThisTime;
@@ -242,30 +467,52 @@ void WinNamedPipeStream::Write(const void *pBuffer, int NBytes)
// --------------------------------------------------------------------------
void WinNamedPipeStream::Close()
{
- if (mSocketHandle == NULL && mIsConnected)
+ if (mSocketHandle == INVALID_HANDLE_VALUE && mIsConnected)
{
- fprintf(stderr, "Inconsistent connected state\n");
- ::syslog(LOG_ERR, "Inconsistent connected state");
+ BOX_ERROR("Named pipe: inconsistent connected state");
mIsConnected = false;
}
- if (mSocketHandle == NULL)
+ if (mSocketHandle == INVALID_HANDLE_VALUE)
{
THROW_EXCEPTION(ServerException, BadSocketHandle)
}
if (mIsServer)
- {
+ {
+ if (!CancelIo(mSocketHandle))
+ {
+ BOX_ERROR("Failed to cancel outstanding I/O: " <<
+ GetErrorMessage(GetLastError()));
+ }
+
+ if (mReadableEvent == INVALID_HANDLE_VALUE)
+ {
+ BOX_ERROR("Failed to destroy Readable event: "
+ "invalid handle");
+ }
+ else if (!CloseHandle(mReadableEvent))
+ {
+ BOX_ERROR("Failed to destroy Readable event: " <<
+ GetErrorMessage(GetLastError()));
+ }
+
+ mReadableEvent = INVALID_HANDLE_VALUE;
+
if (!FlushFileBuffers(mSocketHandle))
{
- ::syslog(LOG_INFO, "FlushFileBuffers failed: %d",
- GetLastError());
+ BOX_ERROR("Failed to FlushFileBuffers: " <<
+ GetErrorMessage(GetLastError()));
}
if (!DisconnectNamedPipe(mSocketHandle))
{
- ::syslog(LOG_ERR, "DisconnectNamedPipe failed: %d",
- GetLastError());
+ DWORD err = GetLastError();
+ if (err != ERROR_PIPE_NOT_CONNECTED)
+ {
+ BOX_ERROR("Failed to DisconnectNamedPipe: " <<
+ GetErrorMessage(err));
+ }
}
mIsServer = false;
@@ -273,12 +520,15 @@ void WinNamedPipeStream::Close()
bool result = CloseHandle(mSocketHandle);
- mSocketHandle = NULL;
+ mSocketHandle = INVALID_HANDLE_VALUE;
mIsConnected = false;
+ mReadClosed = true;
+ mWriteClosed = true;
if (!result)
{
- ::syslog(LOG_ERR, "CloseHandle failed: %d", GetLastError());
+ BOX_ERROR("Failed to CloseHandle: " <<
+ GetErrorMessage(GetLastError()));
THROW_EXCEPTION(ServerException, SocketCloseError)
}
}
@@ -309,4 +559,27 @@ bool WinNamedPipeStream::StreamClosed()
return mWriteClosed;
}
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: IOStream::WriteAllBuffered()
+// Purpose: Ensures that any data which has been buffered is written to the stream
+// Created: 2003/08/26
+//
+// --------------------------------------------------------------------------
+void WinNamedPipeStream::WriteAllBuffered()
+{
+ if (mSocketHandle == INVALID_HANDLE_VALUE || !mIsConnected)
+ {
+ THROW_EXCEPTION(ServerException, BadSocketHandle)
+ }
+
+ if (!FlushFileBuffers(mSocketHandle))
+ {
+ BOX_ERROR("Failed to FlushFileBuffers: " <<
+ GetErrorMessage(GetLastError()));
+ }
+}
+
+
#endif // WIN32
diff --git a/lib/server/WinNamedPipeStream.h b/lib/server/WinNamedPipeStream.h
index 5a800371..aded2d59 100644
--- a/lib/server/WinNamedPipeStream.h
+++ b/lib/server/WinNamedPipeStream.h
@@ -36,13 +36,15 @@ public:
virtual int Read(void *pBuffer, int NBytes,
int Timeout = IOStream::TimeOutInfinite);
virtual void Write(const void *pBuffer, int NBytes);
+ virtual void WriteAllBuffered();
virtual void Close();
virtual bool StreamDataLeft();
virtual bool StreamClosed();
bool IsConnected() { return mIsConnected; }
+ HANDLE GetSocketHandle() { return mSocketHandle; }
+ HANDLE GetReadableEvent() { return mReadableEvent; }
protected:
- HANDLE GetSocketHandle();
void MarkAsReadClosed() {mReadClosed = true;}
void MarkAsWriteClosed() {mWriteClosed = true;}
@@ -51,6 +53,10 @@ private:
{ /* do not call */ }
HANDLE mSocketHandle;
+ HANDLE mReadableEvent;
+ OVERLAPPED mReadOverlap;
+ uint8_t mReadBuffer[4096];
+ size_t mBytesInBuffer;
bool mReadClosed;
bool mWriteClosed;
bool mIsServer;
diff --git a/lib/server/makeprotocol.pl.in b/lib/server/makeprotocol.pl.in
index f89c780d..f4561168 100755
--- a/lib/server/makeprotocol.pl.in
+++ b/lib/server/makeprotocol.pl.in
@@ -1,5 +1,4 @@
#!@PERL@
-
use strict;
use lib "../../infrastructure";
@@ -830,8 +829,8 @@ if($implement_filelog || $implement_syslog)
}
if($implement_filelog)
{
- $fR .= qq~\tif(mLogToFile) { ::fprintf(mLogToFile, (Size==Protocol::ProtocolStream_SizeUncertain)?"Receiving stream, size uncertain":"Receiving stream, size %d\\n", Size); ::fflush(mLogToFile); }\n~;
- $fS .= qq~\tif(mLogToFile) { ::fprintf(mLogToFile, (Size==Protocol::ProtocolStream_SizeUncertain)?"Sending stream, size uncertain":"Sending stream, size %d\\n", Size); ::fflush(mLogToFile); }\n~;
+ $fR .= qq~\tif(mLogToFile) { ::fprintf(mLogToFile, (Size==Protocol::ProtocolStream_SizeUncertain)?"Receiving stream, size uncertain\\n":"Receiving stream, size %d\\n", Size); ::fflush(mLogToFile); }\n~;
+ $fS .= qq~\tif(mLogToFile) { ::fprintf(mLogToFile, (Size==Protocol::ProtocolStream_SizeUncertain)?"Sending stream, size uncertain\\n":"Sending stream, size %d\\n", Size); ::fflush(mLogToFile); }\n~;
}
print CPP <<__E;
@@ -916,6 +915,7 @@ __E
close H;
close CPP;
+
sub obj_is_type
{
my ($c,$ty) = @_;
diff --git a/lib/win32/MSG00001.bin b/lib/win32/MSG00001.bin
new file mode 100755
index 00000000..b4377b85
--- /dev/null
+++ b/lib/win32/MSG00001.bin
Binary files differ
diff --git a/lib/win32/emu.cpp b/lib/win32/emu.cpp
index 91684f1c..071dc788 100644
--- a/lib/win32/emu.cpp
+++ b/lib/win32/emu.cpp
@@ -9,17 +9,16 @@
#include <assert.h>
#include <fcntl.h>
+#include <process.h>
#include <windows.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
-#ifdef HAVE_PROCESS_H
- #include <process.h>
-#endif
#include <string>
#include <list>
+#include <sstream>
// message resource definitions for syslog()
@@ -28,6 +27,7 @@
// our implementation for a timer, based on a
// simple thread which sleeps for a period of time
+static bool gTimerInitialised = false;
static bool gFinishTimer;
static CRITICAL_SECTION gLock;
@@ -43,24 +43,31 @@ static void (__cdecl *gTimerFunc) (int) = NULL;
int setitimer(int type, struct itimerval *timeout, void *arg)
{
- if (ITIMER_VIRTUAL == type)
+ assert(gTimerInitialised);
+
+ if (ITIMER_REAL != type)
{
- EnterCriticalSection(&gLock);
- // we only need seconds for the mo!
- if (timeout->it_value.tv_sec == 0 &&
- timeout->it_value.tv_usec == 0)
- {
- gTimerList.clear();
- }
- else
- {
- Timer_t ourTimer;
- ourTimer.countDown = timeout->it_value.tv_sec;
- ourTimer.interval = timeout->it_interval.tv_sec;
- gTimerList.push_back(ourTimer);
- }
- LeaveCriticalSection(&gLock);
+ errno = ENOSYS;
+ return -1;
+ }
+
+ EnterCriticalSection(&gLock);
+
+ // we only need seconds for the mo!
+ if (timeout->it_value.tv_sec == 0 &&
+ timeout->it_value.tv_usec == 0)
+ {
+ gTimerList.clear();
}
+ else
+ {
+ Timer_t ourTimer;
+ ourTimer.countDown = timeout->it_value.tv_sec;
+ ourTimer.interval = timeout->it_interval.tv_sec;
+ gTimerList.push_back(ourTimer);
+ }
+
+ LeaveCriticalSection(&gLock);
// indicate success
return 0;
@@ -131,20 +138,26 @@ int SetTimerHandler(void (__cdecl *func ) (int))
void InitTimer(void)
{
- InitializeCriticalSection(&gLock);
+ assert(!gTimerInitialised);
+ InitializeCriticalSection(&gLock);
+
// create our thread
HANDLE ourThread = (HANDLE)_beginthreadex(NULL, 0, RunTimer, 0,
CREATE_SUSPENDED, NULL);
SetThreadPriority(ourThread, THREAD_PRIORITY_LOWEST);
ResumeThread(ourThread);
+
+ gTimerInitialised = true;
}
void FiniTimer(void)
{
+ assert(gTimerInitialised);
gFinishTimer = true;
EnterCriticalSection(&gLock);
DeleteCriticalSection(&gLock);
+ gTimerInitialised = false;
}
//Our constants we need to keep track of
@@ -205,6 +218,44 @@ bool EnableBackupRights( void )
}
}
+// forward declaration
+char* ConvertFromWideString(const WCHAR* pString, unsigned int codepage);
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: GetDefaultConfigFilePath(std::string name)
+// Purpose: Calculates the default configuration file name,
+// by using the directory location of the currently
+// executing program, and appending the provided name.
+// In case of fire, returns an empty string.
+// Created: 26th May 2007
+//
+// --------------------------------------------------------------------------
+std::string GetDefaultConfigFilePath(const std::string& rName)
+{
+ WCHAR exePathWide[MAX_PATH];
+ GetModuleFileNameW(NULL, exePathWide, MAX_PATH-1);
+
+ char* exePathUtf8 = ConvertFromWideString(exePathWide, CP_UTF8);
+ if (exePathUtf8 == NULL)
+ {
+ return "";
+ }
+
+ std::string configfile = exePathUtf8;
+ delete [] exePathUtf8;
+
+ // make the default config file name,
+ // based on the program path
+ configfile = configfile.substr(0,
+ configfile.rfind('\\'));
+ configfile += "\\";
+ configfile += rName;
+
+ return configfile;
+}
+
// --------------------------------------------------------------------------
//
// Function
@@ -362,27 +413,34 @@ char* ConvertFromWideString(const WCHAR* pString, unsigned int codepage)
// --------------------------------------------------------------------------
//
// Function
-// Name: ConvertUtf8ToConsole
-// Purpose: Converts a string from UTF-8 to the console
-// code page. On success, replaces contents of rDest
-// and returns true. In case of fire, logs the error
-// and returns false.
-// Created: 4th February 2006
+// Name: ConvertEncoding(const std::string&, int,
+// std::string&, int)
+// Purpose: Converts a string from one code page to another.
+// On success, replaces contents of rDest and returns
+// true. In case of fire, logs the error and returns
+// false.
+// Created: 15th October 2006
//
// --------------------------------------------------------------------------
-bool ConvertUtf8ToConsole(const char* pString, std::string& rDest)
+bool ConvertEncoding(const std::string& rSource, int sourceCodePage,
+ std::string& rDest, int destCodePage)
{
- WCHAR* pWide = ConvertUtf8ToWideString(pString);
+ WCHAR* pWide = ConvertToWideString(rSource.c_str(), sourceCodePage);
if (pWide == NULL)
{
+ ::syslog(LOG_ERR, "Failed to convert string '%s' from "
+ "current code page %d to wide string: %s",
+ rSource.c_str(), sourceCodePage,
+ GetErrorMessage(GetLastError()).c_str());
return false;
}
- char* pConsole = ConvertFromWideString(pWide, GetConsoleOutputCP());
+ char* pConsole = ConvertFromWideString(pWide, destCodePage);
delete [] pWide;
if (!pConsole)
{
+ // Error should have been logged by ConvertFromWideString
return false;
}
@@ -392,39 +450,25 @@ bool ConvertUtf8ToConsole(const char* pString, std::string& rDest)
return true;
}
-// --------------------------------------------------------------------------
-//
-// Function
-// Name: ConvertConsoleToUtf8
-// Purpose: Converts a string from the console code page
-// to UTF-8. On success, replaces contents of rDest
-// and returns true. In case of fire, logs the error
-// and returns false.
-// Created: 4th February 2006
-//
-// --------------------------------------------------------------------------
-bool ConvertConsoleToUtf8(const char* pString, std::string& rDest)
+bool ConvertToUtf8(const char* pString, std::string& rDest, int sourceCodePage)
{
- WCHAR* pWide = ConvertToWideString(pString, GetConsoleCP());
- if (pWide == NULL)
- {
- return false;
- }
-
- char* pConsole = ConvertFromWideString(pWide, CP_UTF8);
- delete [] pWide;
-
- if (!pConsole)
- {
- return false;
- }
+ return ConvertEncoding(pString, sourceCodePage, rDest, CP_UTF8);
+}
- rDest = pConsole;
- delete [] pConsole;
+bool ConvertFromUtf8(const char* pString, std::string& rDest, int destCodePage)
+{
+ return ConvertEncoding(pString, CP_UTF8, rDest, destCodePage);
+}
- return true;
+bool ConvertConsoleToUtf8(const char* pString, std::string& rDest)
+{
+ return ConvertEncoding(pString, GetConsoleCP(), rDest, CP_UTF8);
}
+bool ConvertUtf8ToConsole(const char* pString, std::string& rDest)
+{
+ return ConvertEncoding(pString, CP_UTF8, rDest, GetConsoleOutputCP());
+}
// --------------------------------------------------------------------------
//
@@ -454,23 +498,36 @@ std::string ConvertPathToAbsoluteUnicode(const char *pFileName)
// Is the path relative or absolute?
// Absolute paths on Windows are always a drive letter
// followed by ':'
-
- if (filename.length() >= 2 && filename[1] != ':')
+
+ char wd[PATH_MAX];
+ if (::getcwd(wd, PATH_MAX) == 0)
+ {
+ ::syslog(LOG_WARNING,
+ "Failed to open '%s': path too long",
+ pFileName);
+ errno = ENAMETOOLONG;
+ tmpStr = "";
+ return tmpStr;
+ }
+
+ if (filename.length() > 2 && filename[0] == '\\' &&
+ filename[1] == '\\')
+ {
+ tmpStr += "UNC\\";
+ filename.replace(0, 2, "");
+ // \\?\UNC\<server>\<share>
+ // see http://msdn2.microsoft.com/en-us/library/aa365247.aspx
+ }
+ else if (filename.length() >= 1 && filename[0] == '\\')
+ {
+ // root directory of current drive.
+ tmpStr = wd;
+ tmpStr.resize(2); // drive letter and colon
+ }
+ else if (filename.length() >= 2 && filename[1] != ':')
{
// Must be relative. We need to get the
// current directory to make it absolute.
-
- char wd[PATH_MAX];
- if (::getcwd(wd, PATH_MAX) == 0)
- {
- ::syslog(LOG_WARNING,
- "Failed to open '%s': path too long",
- pFileName);
- errno = ENAMETOOLONG;
- tmpStr = "";
- return tmpStr;
- }
-
tmpStr += wd;
if (tmpStr[tmpStr.length()] != '\\')
{
@@ -482,6 +539,37 @@ std::string ConvertPathToAbsoluteUnicode(const char *pFileName)
return tmpStr;
}
+std::string GetErrorMessage(DWORD errorCode)
+{
+ char* pMsgBuf = NULL;
+
+ DWORD chars = FormatMessage
+ (
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ errorCode,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (char *)(&pMsgBuf),
+ 0, NULL
+ );
+
+ if (chars == 0 || pMsgBuf == NULL)
+ {
+ return std::string("failed to get error message");
+ }
+
+ // remove embedded newline
+ pMsgBuf[chars - 1] = 0;
+ pMsgBuf[chars - 2] = 0;
+
+ std::ostringstream line;
+ line << pMsgBuf << " (" << errorCode << ")";
+ LocalFree(pMsgBuf);
+
+ return line.str();
+}
+
// --------------------------------------------------------------------------
//
// Function
@@ -513,48 +601,62 @@ HANDLE openfile(const char *pFileName, int flags, int mode)
// flags could be O_WRONLY | O_CREAT | O_RDONLY
DWORD createDisposition = OPEN_EXISTING;
- DWORD shareMode = FILE_SHARE_READ;
- DWORD accessRights = FILE_READ_ATTRIBUTES | FILE_LIST_DIRECTORY | FILE_READ_EA;
+ DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE
+ | FILE_SHARE_DELETE;
+ DWORD accessRights = FILE_READ_ATTRIBUTES | FILE_LIST_DIRECTORY
+ | FILE_READ_EA;
if (flags & O_WRONLY)
{
accessRights = FILE_WRITE_DATA;
- shareMode = FILE_SHARE_WRITE;
}
- else if (flags & (O_RDWR | O_CREAT))
+ else if (flags & O_RDWR)
{
accessRights |= FILE_WRITE_ATTRIBUTES
| FILE_WRITE_DATA | FILE_WRITE_EA;
- shareMode |= FILE_SHARE_WRITE;
}
if (flags & O_CREAT)
{
createDisposition = OPEN_ALWAYS;
}
+
if (flags & O_TRUNC)
{
createDisposition = CREATE_ALWAYS;
}
- if (flags & O_EXCL)
+
+ if ((flags & O_CREAT) && (flags & O_EXCL))
+ {
+ createDisposition = CREATE_NEW;
+ }
+
+ if (flags & O_LOCK)
{
shareMode = 0;
}
+ DWORD winFlags = FILE_FLAG_BACKUP_SEMANTICS;
+ if (flags & O_TEMPORARY)
+ {
+ winFlags |= FILE_FLAG_DELETE_ON_CLOSE;
+ }
+
HANDLE hdir = CreateFileW(pBuffer,
accessRights,
shareMode,
NULL,
createDisposition,
- FILE_FLAG_BACKUP_SEMANTICS,
+ winFlags,
NULL);
delete [] pBuffer;
if (hdir == INVALID_HANDLE_VALUE)
{
- ::syslog(LOG_WARNING, "Failed to open file %s: "
- "error %i", pFileName, GetLastError());
+ ::syslog(LOG_WARNING, "Failed to open file '%s': "
+ "%s", pFileName,
+ GetErrorMessage(GetLastError()).c_str());
return INVALID_HANDLE_VALUE;
}
@@ -582,7 +684,7 @@ int emu_fstat(HANDLE hdir, struct stat * st)
if (!GetFileInformationByHandle(hdir, &fi))
{
::syslog(LOG_WARNING, "Failed to read file information: "
- "error %d", GetLastError());
+ "%s", GetErrorMessage(GetLastError()).c_str());
errno = EACCES;
return -1;
}
@@ -590,7 +692,7 @@ int emu_fstat(HANDLE hdir, struct stat * st)
if (INVALID_FILE_ATTRIBUTES == fi.dwFileAttributes)
{
::syslog(LOG_WARNING, "Failed to get file attributes: "
- "error %d", GetLastError());
+ "%s", GetErrorMessage(GetLastError()).c_str());
errno = EACCES;
return -1;
}
@@ -621,7 +723,7 @@ int emu_fstat(HANDLE hdir, struct stat * st)
if (!GetFileSizeEx(hdir, &st_size))
{
::syslog(LOG_WARNING, "Failed to get file size: "
- "error %d", GetLastError());
+ "%s", GetErrorMessage(GetLastError()).c_str());
errno = EACCES;
return -1;
}
@@ -659,6 +761,22 @@ int emu_fstat(HANDLE hdir, struct stat * st)
st->st_mode |= S_IWRITE;
}
+ // st_dev is normally zero, regardless of the drive letter,
+ // since backup locations can't normally span drives. However,
+ // a reparse point does allow all kinds of weird stuff to happen.
+ // We set st_dev to 1 for a reparse point, so that Box will detect
+ // a change of device number (from 0) and refuse to recurse down
+ // the reparse point (which could lead to havoc).
+
+ if (fi.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
+ {
+ st->st_dev = 1;
+ }
+ else
+ {
+ st->st_dev = 0;
+ }
+
return 0;
}
@@ -728,8 +846,9 @@ HANDLE OpenFileByNameUtf8(const char* pFileName, DWORD flags)
}
else
{
- ::syslog(LOG_WARNING,
- "Failed to open '%s': error %d", pFileName, err);
+ ::syslog(LOG_WARNING, "Failed to open '%s': "
+ "%s", pFileName,
+ GetErrorMessage(err).c_str());
errno = EACCES;
}
@@ -797,7 +916,8 @@ int statfs(const char * pName, struct statfs * s)
if (!GetFileInformationByHandle(handle, &fi))
{
::syslog(LOG_WARNING, "Failed to get file information "
- "for '%s': error %d", pName, GetLastError());
+ "for '%s': %s", pName,
+ GetErrorMessage(GetLastError()).c_str());
CloseHandle(handle);
errno = EACCES;
return -1;
@@ -807,7 +927,7 @@ int statfs(const char * pName, struct statfs * s)
_ui64toa(fi.dwVolumeSerialNumber, s->f_mntonname + 1, 16);
// pseudo unix mount point
- s->f_mntonname[0] = DIRECTORY_SEPARATOR_ASCHAR;
+ s->f_mntonname[0] = '\\';
CloseHandle(handle); // close the handle
@@ -850,8 +970,8 @@ int emu_utimes(const char * pName, const struct timeval times[])
if (!SetFileTime(handle, &creationTime, NULL, &modificationTime))
{
- ::syslog(LOG_ERR, "Failed to set times on '%s': error %d",
- pName, GetLastError());
+ ::syslog(LOG_ERR, "Failed to set times on '%s': %s", pName,
+ GetErrorMessage(GetLastError()).c_str());
CloseHandle(handle);
return 1;
}
@@ -893,8 +1013,8 @@ int emu_chmod(const char * pName, mode_t mode)
DWORD attribs = GetFileAttributesW(pBuffer);
if (attribs == INVALID_FILE_ATTRIBUTES)
{
- ::syslog(LOG_ERR, "Failed to get file attributes of '%s': "
- "error %d", pName, GetLastError());
+ ::syslog(LOG_ERR, "Failed to get file attributes of '%s': %s",
+ pName, GetErrorMessage(GetLastError()).c_str());
errno = EACCES;
free(pBuffer);
return -1;
@@ -911,14 +1031,14 @@ int emu_chmod(const char * pName, mode_t mode)
if (!SetFileAttributesW(pBuffer, attribs))
{
- ::syslog(LOG_ERR, "Failed to set file attributes of '%s': "
- "error %d", pName, GetLastError());
+ ::syslog(LOG_ERR, "Failed to set file attributes of '%s': %s",
+ pName, GetErrorMessage(GetLastError()).c_str());
errno = EACCES;
free(pBuffer);
return -1;
}
- free(pBuffer);
+ delete [] pBuffer;
return 0;
}
@@ -1178,15 +1298,15 @@ BOOL AddEventSource
// Work out the executable file name, to register ourselves
// as the event source
- char cmd[MAX_PATH];
- if (GetModuleFileName(NULL, cmd, sizeof(cmd)-1) == 0)
+ WCHAR cmd[MAX_PATH];
+ DWORD len = GetModuleFileNameW(NULL, cmd, MAX_PATH);
+
+ if (len == 0)
{
- ::syslog(LOG_ERR, "Failed to get the program file name: "
- "error %d", GetLastError());
+ ::syslog(LOG_ERR, "Failed to get the program file name: %s",
+ GetErrorMessage(GetLastError()).c_str());
return FALSE;
}
- cmd[sizeof(cmd)-1] = 0;
- std::string exepath(cmd);
// Create the event source as a subkey of the log.
@@ -1201,22 +1321,22 @@ BOOL AddEventSource
0, NULL, REG_OPTION_NON_VOLATILE,
KEY_WRITE, NULL, &hk, &dwDisp))
{
- ::syslog(LOG_ERR, "Failed to create the registry key: "
- "error %d", GetLastError());
+ ::syslog(LOG_ERR, "Failed to create the registry key: %s",
+ GetErrorMessage(GetLastError()).c_str());
return FALSE;
}
// Set the name of the message file.
- if (RegSetValueEx(hk, // subkey handle
- "EventMessageFile", // value name
- 0, // must be zero
- REG_EXPAND_SZ, // value type
- (LPBYTE) exepath.c_str(), // pointer to value data
- (DWORD) (exepath.size()))) // data size
- {
- ::syslog(LOG_ERR, "Failed to set the event message file: "
- "error %d", GetLastError());
+ if (RegSetValueExW(hk, // subkey handle
+ L"EventMessageFile", // value name
+ 0, // must be zero
+ REG_EXPAND_SZ, // value type
+ (LPBYTE)cmd, // pointer to value data
+ len*sizeof(WCHAR))) // data size
+ {
+ ::syslog(LOG_ERR, "Failed to set the event message file: %s",
+ GetErrorMessage(GetLastError()).c_str());
RegCloseKey(hk);
return FALSE;
}
@@ -1233,23 +1353,23 @@ BOOL AddEventSource
(LPBYTE) &dwData, // pointer to value data
sizeof(DWORD))) // length of value data
{
- ::syslog(LOG_ERR, "Failed to set the supported types: "
- "error %d", GetLastError());
+ ::syslog(LOG_ERR, "Failed to set the supported types: %s",
+ GetErrorMessage(GetLastError()).c_str());
RegCloseKey(hk);
return FALSE;
}
// Set the category message file and number of categories.
- if (RegSetValueEx(hk, // subkey handle
- "CategoryMessageFile", // value name
- 0, // must be zero
- REG_EXPAND_SZ, // value type
- (LPBYTE) exepath.c_str(), // pointer to value data
- (DWORD) (exepath.size()))) // data size
+ if (RegSetValueExW(hk, // subkey handle
+ L"CategoryMessageFile", // value name
+ 0, // must be zero
+ REG_EXPAND_SZ, // value type
+ (LPBYTE)cmd, // pointer to value data
+ len*sizeof(WCHAR))) // data size
{
::syslog(LOG_ERR, "Failed to set the category message file: "
- "error %d", GetLastError());
+ "%s", GetErrorMessage(GetLastError()).c_str());
RegCloseKey(hk);
return FALSE;
}
@@ -1261,8 +1381,8 @@ BOOL AddEventSource
(LPBYTE) &dwNum, // pointer to value data
sizeof(DWORD))) // length of value data
{
- ::syslog(LOG_ERR, "Failed to set the category count: "
- "error %d", GetLastError());
+ ::syslog(LOG_ERR, "Failed to set the category count: %s",
+ GetErrorMessage(GetLastError()).c_str());
RegCloseKey(hk);
return FALSE;
}
@@ -1285,17 +1405,21 @@ void openlog(const char * daemonName, int, int)
{
}
- if (!AddEventSource("Box Backup", 0))
+ char* name = strdup(daemonName);
+ BOOL success = AddEventSource(name, 0);
+ free(name);
+
+ if (!success)
{
::syslog(LOG_ERR, "Failed to add our own event source");
return;
}
- HANDLE newSyslogH = RegisterEventSource(NULL, "Box Backup");
+ HANDLE newSyslogH = RegisterEventSource(NULL, daemonName);
if (newSyslogH == NULL)
{
::syslog(LOG_ERR, "Failed to register our own event source: "
- "error %d", GetLastError());
+ "%s", GetErrorMessage(GetLastError()).c_str());
return;
}
@@ -1394,8 +1518,8 @@ void syslog(int loglevel, const char *frmt, ...)
}
else
{
- printf("Unable to send message to Event Log: "
- "error %i:\r\n", (int)err);
+ printf("Unable to send message to Event Log: %s:\r\n",
+ GetErrorMessage(err).c_str());
fflush(stdout);
}
}
@@ -1404,12 +1528,13 @@ void syslog(int loglevel, const char *frmt, ...)
sHaveWarnedEventLogFull = false;
}
- printf("%s\r\n", buffer);
- fflush(stdout);
+ // printf("%s\r\n", buffer);
+ // fflush(stdout);
}
int emu_chdir(const char* pDirName)
{
+ /*
std::string AbsPathWithUnicode =
ConvertPathToAbsoluteUnicode(pDirName);
@@ -1420,11 +1545,19 @@ int emu_chdir(const char* pDirName)
}
WCHAR* pBuffer = ConvertUtf8ToWideString(AbsPathWithUnicode.c_str());
+ */
+
+ WCHAR* pBuffer = ConvertUtf8ToWideString(pDirName);
if (!pBuffer) return -1;
+
int result = SetCurrentDirectoryW(pBuffer);
delete [] pBuffer;
+
if (result != 0) return 0;
+
errno = EACCES;
+ fprintf(stderr, "Failed to change directory to '%s': %s\n",
+ pDirName, GetErrorMessage(GetLastError()).c_str());
return -1;
}
@@ -1454,6 +1587,7 @@ char* emu_getcwd(char* pBuffer, int BufSize)
if (result <= 0 || result >= len)
{
errno = EACCES;
+ delete [] pWide;
return NULL;
}
@@ -1539,7 +1673,74 @@ int emu_unlink(const char* pFileName)
else
{
::syslog(LOG_WARNING, "Failed to delete file "
- "'%s': error %d", pFileName, (int)err);
+ "'%s': %s", pFileName,
+ GetErrorMessage(err).c_str());
+ errno = ENOSYS;
+ }
+ return -1;
+ }
+
+ return 0;
+}
+
+int emu_rename(const char* pOldFileName, const char* pNewFileName)
+{
+ std::string OldPathWithUnicode =
+ ConvertPathToAbsoluteUnicode(pOldFileName);
+
+ if (OldPathWithUnicode.size() == 0)
+ {
+ // error already logged by ConvertPathToAbsoluteUnicode()
+ return -1;
+ }
+
+ WCHAR* pOldBuffer = ConvertUtf8ToWideString(OldPathWithUnicode.c_str());
+ if (!pOldBuffer)
+ {
+ return -1;
+ }
+
+ std::string NewPathWithUnicode =
+ ConvertPathToAbsoluteUnicode(pNewFileName);
+
+ if (NewPathWithUnicode.size() == 0)
+ {
+ // error already logged by ConvertPathToAbsoluteUnicode()
+ delete [] pOldBuffer;
+ return -1;
+ }
+
+ WCHAR* pNewBuffer = ConvertUtf8ToWideString(NewPathWithUnicode.c_str());
+ if (!pNewBuffer)
+ {
+ delete [] pOldBuffer;
+ return -1;
+ }
+
+ BOOL result = MoveFileW(pOldBuffer, pNewBuffer);
+ DWORD err = GetLastError();
+ delete [] pOldBuffer;
+ delete [] pNewBuffer;
+
+ if (!result)
+ {
+ if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND)
+ {
+ errno = ENOENT;
+ }
+ else if (err == ERROR_SHARING_VIOLATION)
+ {
+ errno = EBUSY;
+ }
+ else if (err == ERROR_ACCESS_DENIED)
+ {
+ errno = EACCES;
+ }
+ else
+ {
+ ::syslog(LOG_WARNING, "Failed to rename file "
+ "'%s' to '%s': %s", pOldFileName, pNewFileName,
+ GetErrorMessage(err).c_str());
errno = ENOSYS;
}
return -1;
@@ -1555,12 +1756,12 @@ int console_read(char* pBuffer, size_t BufferSize)
if (hConsole == INVALID_HANDLE_VALUE)
{
::fprintf(stderr, "Failed to get a handle on standard input: "
- "error %d\n", GetLastError());
+ "%s", GetErrorMessage(GetLastError()).c_str());
return -1;
}
size_t WideSize = BufferSize / 5;
- WCHAR* pWideBuffer = new WCHAR [WideSize];
+ WCHAR* pWideBuffer = new WCHAR [WideSize + 1];
if (!pWideBuffer)
{
@@ -1578,14 +1779,16 @@ int console_read(char* pBuffer, size_t BufferSize)
NULL // reserved
))
{
- ::fprintf(stderr, "Failed to read from console: error %d\n",
- GetLastError());
+ ::fprintf(stderr, "Failed to read from console: %s\n",
+ GetErrorMessage(GetLastError()).c_str());
return -1;
}
pWideBuffer[numCharsRead] = 0;
char* pUtf8 = ConvertFromWideString(pWideBuffer, GetConsoleCP());
+ delete [] pWideBuffer;
+
strncpy(pBuffer, pUtf8, BufferSize);
delete [] pUtf8;
@@ -1675,13 +1878,13 @@ bool ConvertTime_tToFileTime(const time_t from, FILETIME *pTo)
// Convert the last-write time to local time.
if (!SystemTimeToFileTime(&stUTC, pTo))
{
- syslog(LOG_ERR, "Failed to convert between time formats: "
- "error %d", GetLastError());
+ syslog(LOG_ERR, "Failed to convert between time formats: %s",
+ GetErrorMessage(GetLastError()).c_str());
return false;
}
return true;
}
-
#endif // WIN32
+
diff --git a/lib/win32/emu.h b/lib/win32/emu.h
index d898968a..8ab74130 100644
--- a/lib/win32/emu.h
+++ b/lib/win32/emu.h
@@ -8,25 +8,24 @@
#ifdef __MINGW32__
#include <stdint.h>
- typedef uint32_t u_int32_t;
#else // MSVC
- typedef __int64 int64_t;
- typedef __int32 int32_t;
- typedef __int16 int16_t;
- typedef __int8 int8_t;
-
typedef unsigned __int64 u_int64_t;
- typedef unsigned __int32 u_int32_t;
-
typedef unsigned __int64 uint64_t;
+ typedef __int64 int64_t;
typedef unsigned __int32 uint32_t;
+ typedef unsigned __int32 u_int32_t;
+ typedef __int32 int32_t;
typedef unsigned __int16 uint16_t;
+ typedef __int16 int16_t;
typedef unsigned __int8 uint8_t;
+ typedef __int8 int8_t;
#endif
// emulated types, present on MinGW but not MSVC or vice versa
-#ifdef _MSC_VER
+#ifdef __MINGW32__
+ typedef uint32_t u_int32_t;
+#else
typedef unsigned int mode_t;
typedef unsigned int pid_t;
@@ -66,7 +65,7 @@
( *(_result) = *gmtime( (_clock) ), \
(_result) )
-#define ITIMER_VIRTUAL 0
+#define ITIMER_REAL 0
#ifdef _MSC_VER
// Microsoft decided to deprecate the standard POSIX functions. Great!
@@ -84,12 +83,6 @@ int setitimer(int type, struct itimerval *timeout, void *arg);
void InitTimer(void);
void FiniTimer(void);
-inline int geteuid(void)
-{
- //lets pretend to be root!
- return 0;
-}
-
struct passwd {
char *pw_name;
char *pw_passwd;
@@ -148,43 +141,9 @@ inline int chown(const char * Filename, u_int32_t uid, u_int32_t gid)
return 0;
}
-int emu_chdir (const char* pDirName);
-int emu_unlink(const char* pFileName);
-char* emu_getcwd(char* pBuffer, int BufSize);
-int emu_utimes(const char* pName, const struct timeval[]);
-int emu_chmod (const char* pName, mode_t mode);
-
-#define utimes(buffer, times) emu_utimes(buffer, times)
-
-#ifdef _MSC_VER
- #define chmod(file, mode) emu_chmod(file, mode)
- #define chdir(directory) emu_chdir(directory)
- #define unlink(file) emu_unlink(file)
- #define getcwd(buffer, size) emu_getcwd(buffer, size)
-#else
- inline int chmod(const char * pName, mode_t mode)
- {
- return emu_chmod(pName, mode);
- }
-
- inline int chdir(const char* pDirName)
- {
- return emu_chdir(pDirName);
- }
-
- inline char* getcwd(char* pBuffer, int BufSize)
- {
- return emu_getcwd(pBuffer, BufSize);
- }
-
- inline int unlink(const char* pFileName)
- {
- return emu_unlink(pFileName);
- }
-#endif
-
-//I do not perceive a need to change the user or group on a backup client
-//at any rate the owner of a service can be set in the service settings
+// Windows and Unix owners and groups are pretty fundamentally different.
+// Ben prefers that we kludge here rather than litter the code with #ifdefs.
+// Pretend to be root, and pretend that set...() operations succeed.
inline int setegid(int)
{
return true;
@@ -209,6 +168,10 @@ inline int getuid(void)
{
return 0;
}
+inline int geteuid(void)
+{
+ return 0;
+}
#ifndef PATH_MAX
#define PATH_MAX MAX_PATH
@@ -235,11 +198,6 @@ struct itimerval
typedef int socklen_t;
#endif
-// I (re-)defined here for the moment; has to be removed later !!!
-#ifndef BOX_VERSION
-#define BOX_VERSION "0.09hWin32"
-#endif
-
#define S_IRGRP S_IWRITE
#define S_IWGRP S_IREAD
#define S_IROTH S_IWRITE | S_IREAD
@@ -252,13 +210,6 @@ struct itimerval
#define vsnprintf _vsnprintf
-int emu_mkdir(const char* pPathName);
-
-inline int mkdir(const char *pPathName, mode_t mode)
-{
- return emu_mkdir(pPathName);
-}
-
#ifndef __MINGW32__
inline int strcasecmp(const char *s1, const char *s2)
{
@@ -290,11 +241,17 @@ DIR *opendir(const char *name);
struct dirent *readdir(DIR *dp);
int closedir(DIR *dp);
+// local constant to open file exclusively without shared access
+#define O_LOCK 0x10000
+
HANDLE openfile(const char *filename, int flags, int mode);
+#define LOG_DEBUG LOG_INFO
#define LOG_INFO 6
+#define LOG_NOTICE LOG_INFO
#define LOG_WARNING 4
#define LOG_ERR 3
+#define LOG_CRIT LOG_ERR
#define LOG_PID 0
#define LOG_LOCAL5 0
#define LOG_LOCAL6 0
@@ -366,43 +323,36 @@ struct stat {
time_t st_mtime;
time_t st_ctime;
};
-#endif
-
-int emu_stat(const char * name, struct stat * st);
-int emu_fstat(HANDLE file, struct stat * st);
-int statfs(const char * name, struct statfs * s);
+#endif // 0
// need this for conversions
time_t ConvertFileTimeToTime_t(FILETIME *fileTime);
bool ConvertTime_tToFileTime(const time_t from, FILETIME *pTo);
-#ifdef _MSC_VER
- #define stat(filename, struct) emu_stat (filename, struct)
- #define lstat(filename, struct) emu_stat (filename, struct)
- #define fstat(handle, struct) emu_fstat(handle, struct)
-#else
- inline int stat(const char* filename, struct stat* stat)
- {
- return emu_stat(filename, stat);
- }
- inline int lstat(const char* filename, struct stat* stat)
- {
- return emu_stat(filename, stat);
- }
- inline int fstat(HANDLE handle, struct stat* stat)
- {
- return emu_fstat(handle, stat);
- }
-#endif
-
-int poll(struct pollfd *ufds, unsigned long nfds, int timeout);
-bool EnableBackupRights( void );
+int emu_chdir (const char* pDirName);
+int emu_mkdir (const char* pPathName);
+int emu_unlink (const char* pFileName);
+int emu_fstat (HANDLE file, struct stat* st);
+int emu_stat (const char* pName, struct stat* st);
+int emu_utimes (const char* pName, const struct timeval[]);
+int emu_chmod (const char* pName, mode_t mode);
+char* emu_getcwd (char* pBuffer, int BufSize);
+int emu_rename (const char* pOldName, const char* pNewName);
+
+#define chdir(directory) emu_chdir (directory)
+#define mkdir(path, mode) emu_mkdir (path)
+#define unlink(file) emu_unlink (file)
+#define stat(filename, struct) emu_stat (filename, struct)
+#define lstat(filename, struct) emu_stat (filename, struct)
+#define fstat(handle, struct) emu_fstat (handle, struct)
+#define utimes(buffer, times) emu_utimes (buffer, times)
+#define chmod(file, mode) emu_chmod (file, mode)
+#define getcwd(buffer, size) emu_getcwd (buffer, size)
+#define rename(oldname, newname) emu_rename (oldname, newname)
-bool ConvertUtf8ToConsole(const char* pString, std::string& rDest);
-bool ConvertConsoleToUtf8(const char* pString, std::string& rDest);
+int statfs(const char * name, struct statfs * s);
-// replacement for _cgetws which requires a relatively recent C runtime lib
-int console_read(char* pBuffer, size_t BufferSize);
+int poll(struct pollfd *ufds, unsigned long nfds, int timeout);
struct iovec {
void *iov_base; /* Starting address */
@@ -412,4 +362,43 @@ struct iovec {
int readv (int filedes, const struct iovec *vector, size_t count);
int writev(int filedes, const struct iovec *vector, size_t count);
+// The following functions are not emulations, but utilities for other
+// parts of the code where Windows API is used or windows-specific stuff
+// is needed, like codepage conversion.
+
+bool EnableBackupRights( void );
+
+bool ConvertEncoding (const std::string& rSource, int sourceCodePage,
+ std::string& rDest, int destCodePage);
+bool ConvertToUtf8 (const std::string& rSource, std::string& rDest,
+ int sourceCodePage);
+bool ConvertFromUtf8 (const std::string& rSource, std::string& rDest,
+ int destCodePage);
+bool ConvertUtf8ToConsole(const char* pString, std::string& rDest);
+bool ConvertConsoleToUtf8(const char* pString, std::string& rDest);
+
+// Utility function which returns a default config file name,
+// based on the path of the current executable.
+std::string GetDefaultConfigFilePath(const std::string& rName);
+
+// GetErrorMessage() returns a system error message, like strerror()
+// but for Windows error codes.
+std::string GetErrorMessage(DWORD errorCode);
+
+// console_read() is a replacement for _cgetws which requires a
+// relatively recent C runtime lib
+int console_read(char* pBuffer, size_t BufferSize);
+
+#ifdef _MSC_VER
+ /* disable certain compiler warnings to be able to actually see the show-stopper ones */
+ #pragma warning(disable:4101) // unreferenced local variable
+ #pragma warning(disable:4244) // conversion, possible loss of data
+ #pragma warning(disable:4267) // conversion, possible loss of data
+ #pragma warning(disable:4311) // pointer truncation
+ #pragma warning(disable:4700) // uninitialized local variable used (hmmmmm...)
+ #pragma warning(disable:4805) // unsafe mix of type and type 'bool' in operation
+ #pragma warning(disable:4800) // forcing value to bool 'true' or 'false' (performance warning)
+ #pragma warning(disable:4996) // POSIX name for this item is deprecated
+#endif // _MSC_VER
+
#endif // !EMU_INCLUDE && WIN32
diff --git a/lib/win32/getopt_long.cxx b/lib/win32/getopt_long.cxx
new file mode 100755
index 00000000..c6800426
--- /dev/null
+++ b/lib/win32/getopt_long.cxx
@@ -0,0 +1,547 @@
+/* $OpenBSD: getopt_long.c,v 1.20 2005/10/25 15:49:37 jmc Exp $ */
+/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */
+// Adapted for Box Backup by Chris Wilson <chris+boxbackup@qwirx.com>
+
+/*
+ * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and 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.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Dieter Baron and Thomas Klausner.
+ *
+ * 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.
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+
+#include "Box.h"
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _MSC_VER
+#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */
+#endif
+
+#ifdef REPLACE_GETOPT
+int opterr = 1; /* if error message should be printed */
+int optind = 1; /* index into parent argv vector */
+int optopt = '?'; /* character checked for validity */
+int optreset; /* reset getopt */
+char *optarg; /* argument associated with option */
+#endif
+
+#define PRINT_ERROR ((opterr) && (*options != ':'))
+
+#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
+#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
+#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
+
+/* return values */
+#define BADCH (int)'?'
+#define BADARG ((*options == ':') ? (int)':' : (int)'?')
+#define INORDER (int)1
+
+#define EMSG ""
+
+static void warnx(const char* fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+}
+
+static int getopt_internal(int, char * const *, const char *,
+ const struct option *, int *, int);
+static int parse_long_options(char * const *, const char *,
+ const struct option *, int *, int);
+static int gcd(int, int);
+static void permute_args(int, int, int, char * const *);
+
+static char *place = EMSG; /* option letter processing */
+
+/* XXX: set optreset to 1 rather than these two */
+static int nonopt_start = -1; /* first non option argument (for permute) */
+static int nonopt_end = -1; /* first option after non options (for permute) */
+
+/* Error messages */
+static const char recargchar[] = "option requires an argument -- %c";
+static const char recargstring[] = "option requires an argument -- %s";
+static const char ambig[] = "ambiguous option -- %.*s";
+static const char noarg[] = "option doesn't take an argument -- %.*s";
+static const char illoptchar[] = "unknown option -- %c";
+static const char illoptstring[] = "unknown option -- %s";
+
+/*
+ * Compute the greatest common divisor of a and b.
+ */
+static int
+gcd(int a, int b)
+{
+ int c;
+
+ c = a % b;
+ while (c != 0) {
+ a = b;
+ b = c;
+ c = a % b;
+ }
+
+ return (b);
+}
+
+/*
+ * Exchange the block from nonopt_start to nonopt_end with the block
+ * from nonopt_end to opt_end (keeping the same order of arguments
+ * in each block).
+ */
+static void
+permute_args(int panonopt_start, int panonopt_end, int opt_end,
+ char * const *nargv)
+{
+ int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
+ char *swap;
+
+ /*
+ * compute lengths of blocks and number and size of cycles
+ */
+ nnonopts = panonopt_end - panonopt_start;
+ nopts = opt_end - panonopt_end;
+ ncycle = gcd(nnonopts, nopts);
+ cyclelen = (opt_end - panonopt_start) / ncycle;
+
+ for (i = 0; i < ncycle; i++) {
+ cstart = panonopt_end+i;
+ pos = cstart;
+ for (j = 0; j < cyclelen; j++) {
+ if (pos >= panonopt_end)
+ pos -= nnonopts;
+ else
+ pos += nopts;
+ swap = nargv[pos];
+ /* LINTED const cast */
+ ((char **) nargv)[pos] = nargv[cstart];
+ /* LINTED const cast */
+ ((char **)nargv)[cstart] = swap;
+ }
+ }
+}
+
+/*
+ * parse_long_options --
+ * Parse long options in argc/argv argument vector.
+ * Returns -1 if short_too is set and the option does not match long_options.
+ */
+static int
+parse_long_options(char * const *nargv, const char *options,
+ const struct option *long_options, int *idx, int short_too)
+{
+ char *current_argv, *has_equal;
+ size_t current_argv_len;
+ int i, match;
+
+ current_argv = place;
+ match = -1;
+
+ optind++;
+
+ if ((has_equal = strchr(current_argv, '=')) != NULL) {
+ /* argument found (--option=arg) */
+ current_argv_len = has_equal - current_argv;
+ has_equal++;
+ } else
+ current_argv_len = strlen(current_argv);
+
+ for (i = 0; long_options[i].name; i++) {
+ /* find matching long option */
+ if (strncmp(current_argv, long_options[i].name,
+ current_argv_len))
+ continue;
+
+ if (strlen(long_options[i].name) == current_argv_len) {
+ /* exact match */
+ match = i;
+ break;
+ }
+ /*
+ * If this is a known short option, don't allow
+ * a partial match of a single character.
+ */
+ if (short_too && current_argv_len == 1)
+ continue;
+
+ if (match == -1) /* partial match */
+ match = i;
+ else {
+ /* ambiguous abbreviation */
+ if (PRINT_ERROR)
+ warnx(ambig, (int)current_argv_len,
+ current_argv);
+ optopt = 0;
+ return (BADCH);
+ }
+ }
+ if (match != -1) { /* option found */
+ if (long_options[match].has_arg == no_argument
+ && has_equal) {
+ if (PRINT_ERROR)
+ warnx(noarg, (int)current_argv_len,
+ current_argv);
+ /*
+ * XXX: GNU sets optopt to val regardless of flag
+ */
+ if (long_options[match].flag == NULL)
+ optopt = long_options[match].val;
+ else
+ optopt = 0;
+ return (BADARG);
+ }
+ if (long_options[match].has_arg == required_argument ||
+ long_options[match].has_arg == optional_argument) {
+ if (has_equal)
+ optarg = has_equal;
+ else if (long_options[match].has_arg ==
+ required_argument) {
+ /*
+ * optional argument doesn't use next nargv
+ */
+ optarg = nargv[optind++];
+ }
+ }
+ if ((long_options[match].has_arg == required_argument)
+ && (optarg == NULL)) {
+ /*
+ * Missing argument; leading ':' indicates no error
+ * should be generated.
+ */
+ if (PRINT_ERROR)
+ warnx(recargstring,
+ current_argv);
+ /*
+ * XXX: GNU sets optopt to val regardless of flag
+ */
+ if (long_options[match].flag == NULL)
+ optopt = long_options[match].val;
+ else
+ optopt = 0;
+ --optind;
+ return (BADARG);
+ }
+ } else { /* unknown option */
+ if (short_too) {
+ --optind;
+ return (-1);
+ }
+ if (PRINT_ERROR)
+ warnx(illoptstring, current_argv);
+ optopt = 0;
+ return (BADCH);
+ }
+ if (idx)
+ *idx = match;
+ if (long_options[match].flag) {
+ *long_options[match].flag = long_options[match].val;
+ return (0);
+ } else
+ return (long_options[match].val);
+}
+
+/*
+ * getopt_internal --
+ * Parse argc/argv argument vector. Called by user level routines.
+ */
+static int
+getopt_internal(int nargc, char * const *nargv, const char *options,
+ const struct option *long_options, int *idx, int flags)
+{
+ const char * oli; /* option letter list index */
+ int optchar, short_too;
+ static int posixly_correct = -1;
+
+ if (options == NULL)
+ return (-1);
+
+ /*
+ * Disable GNU extensions if POSIXLY_CORRECT is set or options
+ * string begins with a '+'.
+ */
+ if (posixly_correct == -1)
+ posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
+ if (posixly_correct || *options == '+')
+ flags &= ~FLAG_PERMUTE;
+ else if (*options == '-')
+ flags |= FLAG_ALLARGS;
+ if (*options == '+' || *options == '-')
+ options++;
+
+ /*
+ * XXX Some GNU programs (like cvs) set optind to 0 instead of
+ * XXX using optreset. Work around this braindamage.
+ */
+ if (optind == 0)
+ optind = optreset = 1;
+
+ optarg = NULL;
+ if (optreset)
+ nonopt_start = nonopt_end = -1;
+start:
+ if (optreset || !*place) { /* update scanning pointer */
+ optreset = 0;
+ if (optind >= nargc) { /* end of argument vector */
+ place = EMSG;
+ if (nonopt_end != -1) {
+ /* do permutation, if we have to */
+ permute_args(nonopt_start, nonopt_end,
+ optind, nargv);
+ optind -= nonopt_end - nonopt_start;
+ }
+ else if (nonopt_start != -1) {
+ /*
+ * If we skipped non-options, set optind
+ * to the first of them.
+ */
+ optind = nonopt_start;
+ }
+ nonopt_start = nonopt_end = -1;
+ return (-1);
+ }
+ if (*(place = nargv[optind]) != '-' ||
+ (place[1] == '\0' && strchr(options, '-') == NULL)) {
+ place = EMSG; /* found non-option */
+ if (flags & FLAG_ALLARGS) {
+ /*
+ * GNU extension:
+ * return non-option as argument to option 1
+ */
+ optarg = nargv[optind++];
+ return (INORDER);
+ }
+ if (!(flags & FLAG_PERMUTE)) {
+ /*
+ * If no permutation wanted, stop parsing
+ * at first non-option.
+ */
+ return (-1);
+ }
+ /* do permutation */
+ if (nonopt_start == -1)
+ nonopt_start = optind;
+ else if (nonopt_end != -1) {
+ permute_args(nonopt_start, nonopt_end,
+ optind, nargv);
+ nonopt_start = optind -
+ (nonopt_end - nonopt_start);
+ nonopt_end = -1;
+ }
+ optind++;
+ /* process next argument */
+ goto start;
+ }
+ if (nonopt_start != -1 && nonopt_end == -1)
+ nonopt_end = optind;
+
+ /*
+ * If we have "-" do nothing, if "--" we are done.
+ */
+ if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
+ optind++;
+ place = EMSG;
+ /*
+ * We found an option (--), so if we skipped
+ * non-options, we have to permute.
+ */
+ if (nonopt_end != -1) {
+ permute_args(nonopt_start, nonopt_end,
+ optind, nargv);
+ optind -= nonopt_end - nonopt_start;
+ }
+ nonopt_start = nonopt_end = -1;
+ return (-1);
+ }
+ }
+
+ /*
+ * Check long options if:
+ * 1) we were passed some
+ * 2) the arg is not just "-"
+ * 3) either the arg starts with -- we are getopt_long_only()
+ */
+ if (long_options != NULL && place != nargv[optind] &&
+ (*place == '-' || (flags & FLAG_LONGONLY))) {
+ short_too = 0;
+ if (*place == '-')
+ place++; /* --foo long option */
+ else if (*place != ':' && strchr(options, *place) != NULL)
+ short_too = 1; /* could be short option too */
+
+ optchar = parse_long_options(nargv, options, long_options,
+ idx, short_too);
+ if (optchar != -1) {
+ place = EMSG;
+ return (optchar);
+ }
+ }
+
+ if ((optchar = (int)*place++) == (int)':' ||
+ optchar == (int)'-' && *place != '\0' ||
+ (oli = strchr(options, optchar)) == NULL) {
+ /*
+ * If the user specified "-" and '-' isn't listed in
+ * options, return -1 (non-option) as per POSIX.
+ * Otherwise, it is an unknown option character (or ':').
+ */
+ if (optchar == (int)'-' && *place == '\0')
+ return (-1);
+ if (!*place)
+ ++optind;
+ if (PRINT_ERROR)
+ warnx(illoptchar, optchar);
+ optopt = optchar;
+ return (BADCH);
+ }
+ if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
+ /* -W long-option */
+ if (*place) /* no space */
+ /* NOTHING */;
+ else if (++optind >= nargc) { /* no arg */
+ place = EMSG;
+ if (PRINT_ERROR)
+ warnx(recargchar, optchar);
+ optopt = optchar;
+ return (BADARG);
+ } else /* white space */
+ place = nargv[optind];
+ optchar = parse_long_options(nargv, options, long_options,
+ idx, 0);
+ place = EMSG;
+ return (optchar);
+ }
+ if (*++oli != ':') { /* doesn't take argument */
+ if (!*place)
+ ++optind;
+ } else { /* takes (optional) argument */
+ optarg = NULL;
+ if (*place) /* no white space */
+ optarg = place;
+ /* XXX: disable test for :: if PC? (GNU doesn't) */
+ else if (oli[1] != ':') { /* arg not optional */
+ if (++optind >= nargc) { /* no arg */
+ place = EMSG;
+ if (PRINT_ERROR)
+ warnx(recargchar, optchar);
+ optopt = optchar;
+ return (BADARG);
+ } else
+ optarg = nargv[optind];
+ } else if (!(flags & FLAG_PERMUTE)) {
+ /*
+ * If permutation is disabled, we can accept an
+ * optional arg separated by whitespace so long
+ * as it does not start with a dash (-).
+ */
+ if (optind + 1 < nargc && *nargv[optind + 1] != '-')
+ optarg = nargv[++optind];
+ }
+ place = EMSG;
+ ++optind;
+ }
+ /* dump back option letter */
+ return (optchar);
+}
+
+#ifdef REPLACE_GETOPT
+/*
+ * getopt --
+ * Parse argc/argv argument vector.
+ *
+ * [eventually this will replace the BSD getopt]
+ */
+int
+getopt(int nargc, char * const *nargv, const char *options)
+{
+
+ /*
+ * We don't pass FLAG_PERMUTE to getopt_internal() since
+ * the BSD getopt(3) (unlike GNU) has never done this.
+ *
+ * Furthermore, since many privileged programs call getopt()
+ * before dropping privileges it makes sense to keep things
+ * as simple (and bug-free) as possible.
+ */
+ return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
+}
+#endif /* REPLACE_GETOPT */
+
+/*
+ * getopt_long --
+ * Parse argc/argv argument vector.
+ */
+int
+getopt_long(int nargc, char * const *nargv, const char *options,
+ const struct option *long_options, int *idx)
+{
+
+ return (getopt_internal(nargc, nargv, options, long_options, idx,
+ FLAG_PERMUTE));
+}
+
+/*
+ * getopt_long_only --
+ * Parse argc/argv argument vector.
+ */
+int
+getopt_long_only(int nargc, char * const *nargv, const char *options,
+ const struct option *long_options, int *idx)
+{
+
+ return (getopt_internal(nargc, nargv, options, long_options, idx,
+ FLAG_PERMUTE|FLAG_LONGONLY));
+}
+