summaryrefslogtreecommitdiff
path: root/bin/bbackupd/BackupDaemon.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'bin/bbackupd/BackupDaemon.cpp')
-rwxr-xr-xbin/bbackupd/BackupDaemon.cpp291
1 files changed, 264 insertions, 27 deletions
diff --git a/bin/bbackupd/BackupDaemon.cpp b/bin/bbackupd/BackupDaemon.cpp
index f22de591..c56aaa59 100755
--- a/bin/bbackupd/BackupDaemon.cpp
+++ b/bin/bbackupd/BackupDaemon.cpp
@@ -9,26 +9,31 @@
#include "Box.h"
+#include <stdio.h>
#include <unistd.h>
-#include <syslog.h>
-#include <sys/param.h>
-#include <signal.h>
+
+#ifndef WIN32
+ #include <signal.h>
+ #include <syslog.h>
+ #include <sys/param.h>
+ #include <sys/wait.h>
+#endif
#ifdef HAVE_SYS_MOUNT_H
- #include <sys/mount.h>
+ #include <sys/mount.h>
#endif
#ifdef HAVE_MNTENT_H
- #include <mntent.h>
-#endif
+ #include <mntent.h>
+#endif
#ifdef HAVE_SYS_MNTTAB_H
#include <cstdio>
#include <sys/mnttab.h>
#endif
-#include <sys/wait.h>
#include "Configuration.h"
#include "IOStream.h"
#include "MemBlockStream.h"
#include "CommonException.h"
+#include "BoxPortsAndFiles.h"
#include "SSLLib.h"
#include "TLSContext.h"
@@ -160,8 +165,9 @@ const ConfigurationVerify *BackupDaemon::GetConfigVerify() const
//
// Function
// Name: BackupDaemon::SetupInInitialProcess()
-// Purpose: Platforms with non-checkable credientals on local sockets only.
-// Prints a warning if the command socket is used.
+// Purpose: Platforms with non-checkable credentials on
+// local sockets only.
+// Prints a warning if the command socket is used.
// Created: 25/2/04
//
// --------------------------------------------------------------------------
@@ -206,6 +212,133 @@ void BackupDaemon::DeleteAllLocations()
mIDMapMounts.clear();
}
+#ifdef WIN32
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: HelperThread()
+// Purpose: Background thread function, called by Windows,
+// calls the BackupDaemon's RunHelperThread method
+// to listen for and act on control communications
+// Created: 18/2/04
+//
+// --------------------------------------------------------------------------
+unsigned int WINAPI HelperThread( LPVOID lpParam )
+{
+ printf( "Parameter = %lu.\n", *(DWORD*)lpParam );
+ ((BackupDaemon *)lpParam)->RunHelperThread();
+
+ return 0;
+}
+
+void BackupDaemon::RunHelperThread(void)
+{
+ mpCommandSocketInfo = new CommandSocketInfo;
+ this->mReceivedCommandConn = false;
+
+ while ( !IsTerminateWanted() )
+ {
+ try
+ {
+ mpCommandSocketInfo->mListeningSocket.Accept(
+ BOX_NAMED_PIPE_NAME);
+
+ // This next section comes from Ben's original function
+ // Log
+ ::syslog(LOG_INFO, "Connection from command socket");
+
+ // Send a header line summarising the configuration
+ // and current state
+ const Configuration &conf(GetConfiguration());
+ char summary[256];
+ size_t summarySize = sprintf(summary,
+ "bbackupd: %d %d %d %d\nstate %d\n",
+ conf.GetKeyValueBool("AutomaticBackup"),
+ conf.GetKeyValueInt("UpdateStoreInterval"),
+ conf.GetKeyValueInt("MinimumFileAge"),
+ conf.GetKeyValueInt("MaxUploadWait"),
+ mState);
+
+ mpCommandSocketInfo->mListeningSocket.Write(summary, summarySize);
+ mpCommandSocketInfo->mListeningSocket.Write("ping\n", 5);
+
+ IOStreamGetLine readLine(mpCommandSocketInfo->mListeningSocket);
+ std::string command;
+
+ while (mpCommandSocketInfo->mListeningSocket.IsConnected() &&
+ readLine.GetLine(command) )
+ {
+ TRACE1("Receiving command '%s' over "
+ "command socket\n", command.c_str());
+
+ bool sendOK = false;
+ bool sendResponse = true;
+ bool disconnect = false;
+
+ // Command to process!
+ if(command == "quit" || command == "")
+ {
+ // Close the socket.
+ disconnect = true;
+ sendResponse = false;
+ }
+ else if(command == "sync")
+ {
+ // Sync now!
+ this->mDoSyncFlagOut = true;
+ this->mSyncIsForcedOut = false;
+ sendOK = true;
+ }
+ else if(command == "force-sync")
+ {
+ // Sync now (forced -- overrides any SyncAllowScript)
+ this->mDoSyncFlagOut = true;
+ this->mSyncIsForcedOut = true;
+ sendOK = true;
+ }
+ else if(command == "reload")
+ {
+ // Reload the configuration
+ SetReloadConfigWanted();
+ sendOK = true;
+ }
+ else if(command == "terminate")
+ {
+ // Terminate the daemon cleanly
+ SetTerminateWanted();
+ sendOK = true;
+ }
+
+ // Send a response back?
+ if (sendResponse)
+ {
+ const char* response = sendOK ? "ok\n" : "error\n";
+ mpCommandSocketInfo->mListeningSocket.Write(
+ response, strlen(response));
+ }
+
+ if (disconnect)
+ {
+ break;
+ }
+
+ this->mReceivedCommandConn = true;
+ }
+
+ mpCommandSocketInfo->mListeningSocket.Close();
+ }
+ catch (BoxException &e)
+ {
+ ::syslog(LOG_ERR, "Communication error with "
+ "control client: %s", e.what());
+ }
+ catch (...)
+ {
+ ::syslog(LOG_ERR, "Communication error with control client");
+ }
+ }
+}
+#endif
// --------------------------------------------------------------------------
//
@@ -217,6 +350,25 @@ void BackupDaemon::DeleteAllLocations()
// --------------------------------------------------------------------------
void BackupDaemon::Run()
{
+#ifdef WIN32
+
+ // Create a thread to handle the named pipe
+ HANDLE hThread;
+ unsigned int dwThreadId;
+
+ hThread = (HANDLE) _beginthreadex(
+ NULL, // default security attributes
+ 0, // use default stack size
+ HelperThread, // thread function
+ this, // argument to thread function
+ 0, // use default creation flags
+ &dwThreadId); // returns the thread identifier
+
+ // init our own timer for file diff timeouts
+ InitTimer();
+
+#else // ! WIN32
+
// Ignore SIGPIPE (so that if a command connection is broken, the daemon doesn't terminate)
::signal(SIGPIPE, SIG_IGN);
@@ -230,6 +382,8 @@ void BackupDaemon::Run()
::unlink(socketName);
mpCommandSocketInfo->mListeningSocket.Listen(Socket::TypeUNIX, socketName);
}
+
+#endif // WIN32
// Handle things nicely on exceptions
try
@@ -253,6 +407,11 @@ void BackupDaemon::Run()
delete mpCommandSocketInfo;
mpCommandSocketInfo = 0;
}
+
+#ifdef WIN32
+ // clean up windows specific stuff.
+ FiniTimer();
+#endif
}
// --------------------------------------------------------------------------
@@ -303,9 +462,9 @@ void BackupDaemon::Run2()
// When the last sync started (only updated if the store was not full when the sync ended)
box_time_t lastSyncTime = 0;
-
- // --------------------------------------------------------------------------------------------
+ // --------------------------------------------------------------------------------------------
+
// And what's the current client store marker?
int64_t clientStoreMarker = BackupClientContext::ClientStoreMarker_NotKnown; // haven't contacted the store yet
@@ -409,6 +568,8 @@ void BackupDaemon::Run2()
// Do sync
bool errorOccurred = false;
int errorCode = 0, errorSubCode = 0;
+ const char* errorString = "unknown";
+
try
{
// Set state and log start
@@ -512,6 +673,7 @@ void BackupDaemon::Run2()
catch(BoxException &e)
{
errorOccurred = true;
+ errorString = e.what();
errorCode = e.GetType();
errorSubCode = e.GetSubType();
}
@@ -555,8 +717,13 @@ void BackupDaemon::Run2()
{
// Not restart/terminate, pause and retry
SetState(State_Error);
- ::syslog(LOG_ERR, "Exception caught (%d/%d), reset state and waiting to retry...", errorCode, errorSubCode);
- ::sleep(100);
+ ::syslog(LOG_ERR,
+ "Exception caught (%s %d/%d), "
+ "reset state and waiting "
+ "to retry...",
+ errorString, errorCode,
+ errorSubCode);
+ ::sleep(10);
}
}
@@ -665,6 +832,29 @@ int BackupDaemon::UseScriptToSeeIfSyncAllowed()
// --------------------------------------------------------------------------
void BackupDaemon::WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFlagOut, bool &SyncIsForcedOut)
{
+#ifdef WIN32
+ // Really could use some interprocess protection, mutex etc
+ // any side effect should be too bad???? :)
+ DWORD timeout = BoxTimeToMilliSeconds(RequiredDelay);
+
+ while ( this->mReceivedCommandConn == false )
+ {
+ Sleep(1);
+
+ if ( timeout == 0 )
+ {
+ DoSyncFlagOut = false;
+ SyncIsForcedOut = false;
+ return;
+ }
+ timeout--;
+ }
+ this->mReceivedCommandConn = false;
+ DoSyncFlagOut = this->mDoSyncFlagOut;
+ SyncIsForcedOut = this->mSyncIsForcedOut;
+
+ return;
+#else // ! WIN32
ASSERT(mpCommandSocketInfo != 0);
if(mpCommandSocketInfo == 0) {::sleep(1); return;} // failure case isn't too bad
@@ -713,9 +903,9 @@ void BackupDaemon::WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFla
}
}
}
-#endif
+#endif // PLATFORM_CANNOT_FIND_PEER_UID_OF_UNIX_SOCKET
- // Is this an acceptible connection?
+ // Is this an acceptable connection?
if(!uidOK)
{
// Dump the connection
@@ -833,6 +1023,7 @@ void BackupDaemon::WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFla
CloseCommandConnection();
}
}
+#endif // WIN32
}
@@ -849,13 +1040,17 @@ void BackupDaemon::CloseCommandConnection()
try
{
TRACE0("Closing command connection\n");
-
+
+#ifdef WIN32
+ mpCommandSocketInfo->mListeningSocket.Close();
+#else
if(mpCommandSocketInfo->mpGetLine)
{
delete mpCommandSocketInfo->mpGetLine;
mpCommandSocketInfo->mpGetLine = 0;
}
mpCommandSocketInfo->mpConnectedSocket.reset();
+#endif
}
catch(...)
{
@@ -875,15 +1070,27 @@ void BackupDaemon::CloseCommandConnection()
// --------------------------------------------------------------------------
void BackupDaemon::SendSyncStartOrFinish(bool SendStart)
{
-
// The bbackupctl program can't rely on a state change, because it may never
// change if the server doesn't need to be contacted.
- if(mpCommandSocketInfo != 0 && mpCommandSocketInfo->mpConnectedSocket.get() != 0)
+ if (mpCommandSocketInfo != NULL &&
+#ifdef WIN32
+ mpCommandSocketInfo->mListeningSocket.IsConnected()
+#else
+ mpCommandSocketInfo->mpConnectedSocket.get() != 0
+#endif
+ )
{
+ const char* message = SendStart ? "start-sync\n" : "finish-sync\n";
try
{
- mpCommandSocketInfo->mpConnectedSocket->Write(SendStart?"start-sync\n":"finish-sync\n", SendStart?11:12);
+#ifdef WIN32
+ mpCommandSocketInfo->mListeningSocket.Write(message,
+ strlen(message));
+#else
+ mpCommandSocketInfo->mpConnectedSocket->Write(message,
+ strlen(message));
+#endif
}
catch(...)
{
@@ -958,6 +1165,7 @@ void BackupDaemon::SetupLocations(BackupClientContext &rClientContext, const Con
std::map<std::string, int> mounts;
int numIDMaps = 0;
+#ifdef HAVE_MOUNTS
#ifndef HAVE_STRUCT_STATFS_F_MNTONNAME
// Linux and others can't tell you where a directory is mounted. So we
// have to read the mount entries from /etc/mtab! Bizarre that the OS
@@ -996,7 +1204,7 @@ void BackupDaemon::SetupLocations(BackupClientContext &rClientContext, const Con
::endmntent(mountPointsFile);
throw;
}
-#else
+#else // ! HAVE_STRUCT_MNTENT_MNT_DIR
// Open mounts file
mountPointsFile = ::fopen("/etc/mnttab", "r");
if(mountPointsFile == 0)
@@ -1023,7 +1231,7 @@ void BackupDaemon::SetupLocations(BackupClientContext &rClientContext, const Con
::fclose(mountPointsFile);
throw;
}
-#endif
+#endif // HAVE_STRUCT_MNTENT_MNT_DIR
// Check sorting and that things are as we expect
ASSERT(mountPoints.size() > 0);
#ifndef NDEBUG
@@ -1033,6 +1241,7 @@ void BackupDaemon::SetupLocations(BackupClientContext &rClientContext, const Con
}
#endif // n NDEBUG
#endif // n HAVE_STRUCT_STATFS_F_MNTONNAME
+#endif // HAVE_MOUNTS
// Then... go through each of the entries in the configuration,
// making sure there's a directory created for it.
@@ -1054,7 +1263,9 @@ TRACE0("new location\n");
// Do a fsstat on the pathname to find out which mount it's on
{
-#ifdef HAVE_STRUCT_STATFS_F_MNTONNAME
+
+#if defined HAVE_STRUCT_STATFS_F_MNTONNAME || defined WIN32
+
// BSD style statfs -- includes mount point, which is nice.
struct statfs s;
if(::statfs(ploc->mPath.c_str(), &s) != 0)
@@ -1064,7 +1275,9 @@ TRACE0("new location\n");
// Where the filesystem is mounted
std::string mountName(s.f_mntonname);
-#else
+
+#else // !HAVE_STRUCT_STATFS_F_MNTONNAME && !WIN32
+
// Warn in logs if the directory isn't absolute
if(ploc->mPath[0] != '/')
{
@@ -1090,6 +1303,7 @@ TRACE0("new location\n");
}
TRACE2("mount point chosen for %s is %s\n", ploc->mPath.c_str(), mountName.c_str());
}
+
#endif
// Got it?
@@ -1376,6 +1590,10 @@ void BackupDaemon::CommitIDMapsAfterSync()
std::string newmap(target + ".n");
// Try to rename
+#ifdef WIN32
+ // win32 rename doesn't overwrite existing files
+ ::remove(target.c_str());
+#endif
if(::rename(newmap.c_str(), target.c_str()) != 0)
{
THROW_EXCEPTION(CommonException, OSFileError)
@@ -1457,14 +1675,33 @@ void BackupDaemon::SetState(int State)
// Set process title
const static char *stateText[] = {"idle", "connected", "error -- waiting for retry", "over limit on server -- not backing up"};
SetProcessTitle(stateText[State]);
-
+
// If there's a command socket connected, then inform it -- disconnecting from the
// command socket if there's an error
+
+ char newState[64];
+ char newStateSize = sprintf(newState, "state %d\n", State);
+
+#ifdef WIN32
+ #warning FIX ME: race condition
+ // what happens if the socket is closed by the other thread before
+ // we can write to it? Null pointer deref at best.
+ if (mpCommandSocketInfo &&
+ mpCommandSocketInfo->mListeningSocket.IsConnected())
+ {
+ try
+ {
+ mpCommandSocketInfo->mListeningSocket.Write(newState, newStateSize);
+ }
+ catch(...)
+ {
+ CloseCommandConnection();
+ }
+ }
+#else
if(mpCommandSocketInfo != 0 && mpCommandSocketInfo->mpConnectedSocket.get() != 0)
{
// Something connected to the command socket, tell it about the new state
- char newState[64];
- char newStateSize = sprintf(newState, "state %d\n", State);
try
{
mpCommandSocketInfo->mpConnectedSocket->Write(newState, newStateSize);
@@ -1474,6 +1711,7 @@ void BackupDaemon::SetState(int State)
CloseCommandConnection();
}
}
+#endif
}
@@ -1658,4 +1896,3 @@ BackupDaemon::CommandSocketInfo::~CommandSocketInfo()
mpGetLine = 0;
}
}
-