diff options
Diffstat (limited to 'bin/bbackupd')
-rw-r--r-- | bin/bbackupd/BackupClientContext.cpp | 2 | ||||
-rw-r--r-- | bin/bbackupd/BackupClientDirectoryRecord.cpp | 98 | ||||
-rw-r--r-- | bin/bbackupd/BackupDaemon.cpp | 282 | ||||
-rw-r--r-- | bin/bbackupd/BackupDaemon.h | 14 | ||||
-rw-r--r-- | bin/bbackupd/Win32BackupService.cpp | 47 | ||||
-rw-r--r-- | bin/bbackupd/Win32BackupService.h | 2 | ||||
-rw-r--r-- | bin/bbackupd/Win32ServiceFunctions.cpp | 184 | ||||
-rw-r--r-- | bin/bbackupd/Win32ServiceFunctions.h | 6 | ||||
-rw-r--r-- | bin/bbackupd/bbackupd.cpp | 56 |
9 files changed, 481 insertions, 210 deletions
diff --git a/bin/bbackupd/BackupClientContext.cpp b/bin/bbackupd/BackupClientContext.cpp index 25852b19..4b84fecb 100644 --- a/bin/bbackupd/BackupClientContext.cpp +++ b/bin/bbackupd/BackupClientContext.cpp @@ -176,7 +176,7 @@ BackupProtocolClient &BackupClientContext::GetConnection() // no -- flag so only things like deletions happen mStorageLimitExceeded = true; // Log - ::syslog(LOG_INFO, "Exceeded storage limits on server -- not uploading changes to files"); + ::syslog(LOG_WARNING, "Exceeded storage limits on server -- not uploading changes to files"); } } catch(...) diff --git a/bin/bbackupd/BackupClientDirectoryRecord.cpp b/bin/bbackupd/BackupClientDirectoryRecord.cpp index 5a566d84..37bd3fea 100644 --- a/bin/bbackupd/BackupClientDirectoryRecord.cpp +++ b/bin/bbackupd/BackupClientDirectoryRecord.cpp @@ -645,34 +645,75 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP // Need to update? // // Condition for upload: - // modifiction time within sync period + // modification time within sync period // if it's been seen before but not uploaded, is the time from this first sight longer than the MaxUploadWait // and if we know about it from a directory listing, that it hasn't got the same upload time as on the store - if( - ( - // Check the file modified within the acceptable time period we're checking - // If the file isn't on the server, the acceptable time starts at zero. - // Check pDirOnStore and en, because if we didn't download a directory listing, - // pDirOnStore will be zero, but we know it's on the server. - ( ((pDirOnStore != 0 && en == 0) || (modTime >= rParams.mSyncPeriodStart)) && modTime < rParams.mSyncPeriodEnd) - - // However, just in case things are continually modified, we check the first seen time. - // The two compares of syncPeriodEnd and pendingFirstSeenTime are because the values are unsigned. - || (pendingFirstSeenTime != 0 && - (rParams.mSyncPeriodEnd > pendingFirstSeenTime) - && ((rParams.mSyncPeriodEnd - pendingFirstSeenTime) > rParams.mMaxUploadWait)) - - // Then make sure that if files are added with a time less than the sync period start - // (which can easily happen on file server), it gets uploaded. The directory contents checksum - // will pick up the fact it has been added, so the store listing will be available when this happens. - || ((modTime <= rParams.mSyncPeriodStart) && (en != 0) && (en->GetModificationTime() != modTime)) - - // And just to catch really badly off clocks in the future for file server clients, - // just upload the file if it's madly in the future. - || (modTime > rParams.mUploadAfterThisTimeInTheFuture) - ) - // But even then, only upload it if the mod time locally is different to that on the server. - && (en == 0 || en->GetModificationTime() != modTime)) + + bool doUpload = false; + + // Only upload a file if the mod time locally is + // different to that on the server. + + if (en == 0 || en->GetModificationTime() != modTime) + { + // Check the file modified within the acceptable time period we're checking + // If the file isn't on the server, the acceptable time starts at zero. + // Check pDirOnStore and en, because if we didn't download a directory listing, + // pDirOnStore will be zero, but we know it's on the server. + if (modTime < rParams.mSyncPeriodEnd) + { + if (pDirOnStore != 0 && en == 0) + { + doUpload = true; + } + else if (modTime >= rParams.mSyncPeriodStart) + { + doUpload = true; + } + } + + // However, just in case things are continually + // modified, we check the first seen time. + // The two compares of syncPeriodEnd and + // pendingFirstSeenTime are because the values + // are unsigned. + + if (!doUpload && + pendingFirstSeenTime != 0 && + rParams.mSyncPeriodEnd > pendingFirstSeenTime && + (rParams.mSyncPeriodEnd - pendingFirstSeenTime) + > rParams.mMaxUploadWait) + { + doUpload = true; + } + + // Then make sure that if files are added with a + // time less than the sync period start + // (which can easily happen on file server), it + // gets uploaded. The directory contents checksum + // will pick up the fact it has been added, so the + // store listing will be available when this happens. + + if (!doUpload && + modTime <= rParams.mSyncPeriodStart && + en != 0 && + en->GetModificationTime() != modTime) + { + doUpload = true; + } + + // And just to catch really badly off clocks in + // the future for file server clients, + // just upload the file if it's madly in the future. + + if (!doUpload && modTime > + rParams.mUploadAfterThisTimeInTheFuture) + { + doUpload = true; + } + } + + if (doUpload) { // Make sure we're connected -- must connect here so we know whether // the storage limit has been exceeded, and hence whether or not @@ -1022,7 +1063,10 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP BackupClientDirectoryRecord *rec = e->second; mSubDirectories.erase(e); delete rec; - TRACE2("Deleted directory record for %s/%s\n", rLocalPath.c_str(), dirname.GetClearFilename().c_str()); + TRACE2("Deleted directory record for " + "%s" DIRECTORY_SEPARATOR "%s\n", + rLocalPath.c_str(), + dirname.GetClearFilename().c_str()); } } } diff --git a/bin/bbackupd/BackupDaemon.cpp b/bin/bbackupd/BackupDaemon.cpp index ebf5a1ea..e2d7c5e7 100644 --- a/bin/bbackupd/BackupDaemon.cpp +++ b/bin/bbackupd/BackupDaemon.cpp @@ -125,6 +125,29 @@ BackupDaemon::BackupDaemon() } #ifdef WIN32 + // Create the event object to signal from main thread to worker + // when new messages are queued to be sent to the command socket. + mhMessageToSendEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (mhMessageToSendEvent == INVALID_HANDLE_VALUE) + { + syslog(LOG_ERR, "Failed to create event object: error %d", + GetLastError); + exit(1); + } + + // Create the event object to signal from worker to main thread + // when a command has been received on the command socket. + mhCommandReceivedEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (mhCommandReceivedEvent == INVALID_HANDLE_VALUE) + { + syslog(LOG_ERR, "Failed to create event object: error %d", + GetLastError); + exit(1); + } + + // Create the critical section to protect the message queue + InitializeCriticalSection(&mMessageQueueLock); + // Create a thread to handle the named pipe HANDLE hThread; unsigned int dwThreadId; @@ -263,15 +286,34 @@ void BackupDaemon::DeleteAllLocations() void BackupDaemon::RunHelperThread(void) { mpCommandSocketInfo = new CommandSocketInfo; - this->mReceivedCommandConn = false; + WinNamedPipeStream& rSocket(mpCommandSocketInfo->mListeningSocket); // loop until the parent process exits - while (TRUE) + while (!IsTerminateWanted()) { try { - mpCommandSocketInfo->mListeningSocket.Accept( - BOX_NAMED_PIPE_NAME); + rSocket.Accept(BOX_NAMED_PIPE_NAME); + } + catch (BoxException &e) + { + ::syslog(LOG_ERR, "Failed to open command socket: %s", + e.what()); + SetTerminateWanted(); + break; // this is fatal + } + catch (...) + { + ::syslog(LOG_ERR, "Failed to open command socket: " + "unknown error"); + SetTerminateWanted(); + break; // this is fatal + } + + try + { + // Errors here do not kill the thread, + // only the current connection. // This next section comes from Ben's original function // Log @@ -289,17 +331,72 @@ void BackupDaemon::RunHelperThread(void) conf.GetKeyValueInt("MaxUploadWait"), mState); - mpCommandSocketInfo->mListeningSocket.Write(summary, summarySize); - mpCommandSocketInfo->mListeningSocket.Write("ping\n", 5); + rSocket.Write(summary, summarySize); + rSocket.Write("ping\n", 5); + + // old queued messages are not useful + EnterCriticalSection(&mMessageQueueLock); + mMessageList.clear(); + ResetEvent(mhMessageToSendEvent); + LeaveCriticalSection(&mMessageQueueLock); - IOStreamGetLine readLine(mpCommandSocketInfo->mListeningSocket); + IOStreamGetLine readLine(rSocket); std::string command; - while (mpCommandSocketInfo->mListeningSocket.IsConnected() && - readLine.GetLine(command) ) + while (rSocket.IsConnected() && !IsTerminateWanted()) { - TRACE1("Receiving command '%s' over " - "command socket\n", command.c_str()); + HANDLE handles[2]; + handles[0] = mhMessageToSendEvent; + handles[1] = rSocket.GetReadableEvent(); + + DWORD result = WaitForMultipleObjects( + sizeof(handles)/sizeof(*handles), + handles, FALSE, 1000); + + if (result == 0) + { + ResetEvent(mhMessageToSendEvent); + + EnterCriticalSection(&mMessageQueueLock); + try + { + while (mMessageList.size() > 0) + { + std::string message = *(mMessageList.begin()); + mMessageList.erase(mMessageList.begin()); + printf("Sending '%s' to waiting client... ", message.c_str()); + message += "\n"; + rSocket.Write(message.c_str(), + message.length()); + + printf("done.\n"); + } + } + catch (...) + { + LeaveCriticalSection(&mMessageQueueLock); + throw; + } + LeaveCriticalSection(&mMessageQueueLock); + continue; + } + else if (result == WAIT_TIMEOUT) + { + continue; + } + else if (result != 1) + { + ::syslog(LOG_ERR, "WaitForMultipleObjects returned invalid result %d", result); + continue; + } + + if (!readLine.GetLine(command)) + { + ::syslog(LOG_ERR, "Failed to read line"); + continue; + } + + printf("Received command '%s' from client\n", command.c_str()); bool sendOK = false; bool sendResponse = true; @@ -338,12 +435,18 @@ void BackupDaemon::RunHelperThread(void) SetTerminateWanted(); sendOK = true; } + else + { + ::syslog(LOG_ERR, "Received unknown command '%s' from client", command.c_str()); + sendResponse = true; + sendOK = false; + } // Send a response back? if (sendResponse) { const char* response = sendOK ? "ok\n" : "error\n"; - mpCommandSocketInfo->mListeningSocket.Write( + rSocket.Write( response, strlen(response)); } @@ -352,10 +455,10 @@ void BackupDaemon::RunHelperThread(void) break; } - this->mReceivedCommandConn = true; + // this->mReceivedCommandConn = true; } - mpCommandSocketInfo->mListeningSocket.Close(); + rSocket.Close(); } catch (BoxException &e) { @@ -504,8 +607,8 @@ void BackupDaemon::Run2() BackupClientContext::ClientStoreMarker_NotKnown; // haven't contacted the store yet - bool deserialised = DeserializeStoreObjectInfo(clientStoreMarker, - lastSyncTime, nextSyncTime); + bool deleteStoreObjectInfoFile = DeserializeStoreObjectInfo( + clientStoreMarker, lastSyncTime, nextSyncTime); // -------------------------------------------------------------------------------------------- @@ -611,7 +714,8 @@ void BackupDaemon::Run2() // Delete the serialised store object file, // so that we don't try to reload it after a // partially completed backup - if(deserialised && !DeleteStoreObjectInfo()) + if(deleteStoreObjectInfoFile && + !DeleteStoreObjectInfo()) { ::syslog(LOG_ERR, "Failed to delete the " "StoreObjectInfoFile, backup cannot " @@ -621,6 +725,11 @@ void BackupDaemon::Run2() ::sleep(60); continue; } + + // In case the backup throws an exception, + // we should not try to delete the store info + // object file again. + deleteStoreObjectInfoFile = false; // Do sync bool errorOccurred = false; @@ -729,8 +838,14 @@ void BackupDaemon::Run2() // -------------------------------------------------------------------------------------------- - // We had a successful backup, save the store info - SerializeStoreObjectInfo(clientStoreMarker, lastSyncTime, nextSyncTime); + // We had a successful backup, save the store + // info. If we save successfully, we must + // delete the file next time we start a backup + + deleteStoreObjectInfoFile = + SerializeStoreObjectInfo( + clientStoreMarker, + lastSyncTime, nextSyncTime); // -------------------------------------------------------------------------------------------- } @@ -902,25 +1017,27 @@ 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 = (DWORD)BoxTimeToMilliSeconds(RequiredDelay); + DWORD requiredDelayMs = BoxTimeToMilliSeconds(RequiredDelay); - while ( this->mReceivedCommandConn == false ) - { - Sleep(1); + DWORD result = WaitForSingleObject(mhCommandReceivedEvent, + (DWORD)requiredDelayMs); - if ( timeout == 0 ) - { - DoSyncFlagOut = false; - SyncIsForcedOut = false; - return; - } - timeout--; + if (result == WAIT_OBJECT_0) + { + DoSyncFlagOut = this->mDoSyncFlagOut; + SyncIsForcedOut = this->mSyncIsForcedOut; + ResetEvent(mhCommandReceivedEvent); + } + else if (result == WAIT_TIMEOUT) + { + DoSyncFlagOut = false; + SyncIsForcedOut = false; + } + else + { + ::syslog(LOG_ERR, "Unexpected result from " + "WaitForSingleObject: error %d", GetLastError()); } - this->mReceivedCommandConn = false; - DoSyncFlagOut = this->mDoSyncFlagOut; - SyncIsForcedOut = this->mSyncIsForcedOut; return; #else // ! WIN32 @@ -953,7 +1070,7 @@ void BackupDaemon::WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFla { #ifdef PLATFORM_CANNOT_FIND_PEER_UID_OF_UNIX_SOCKET bool uidOK = true; - ::syslog(LOG_WARNING, "On this platform, no security check can be made on the credientials of peers connecting to the command socket. (bbackupctl)"); + ::syslog(LOG_WARNING, "On this platform, no security check can be made on the credentials of peers connecting to the command socket. (bbackupctl)"); #else // Security check -- does the process connecting to this socket have // the same UID as this process? @@ -1023,8 +1140,13 @@ void BackupDaemon::WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFla while(mpCommandSocketInfo->mpGetLine != 0 && !mpCommandSocketInfo->mpGetLine->IsEOF() && mpCommandSocketInfo->mpGetLine->GetLine(command, false /* no preprocessing */, timeout)) { - TRACE1("Receiving command '%s' over command socket\n", command.c_str()); - + TRACE1("Receiving command '%s' over command socket\n", + command.c_str()); + + #ifdef WIN32 + SetEvent(mhCommandReceivedEvent); + #endif + bool sendOK = false; bool sendResponse = true; @@ -1137,12 +1259,8 @@ 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. - -#ifdef __MINGW32__ -#warning race condition: what happens if socket is closed? -#endif + // 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 != NULL && #ifdef WIN32 @@ -1152,15 +1270,18 @@ void BackupDaemon::SendSyncStartOrFinish(bool SendStart) #endif ) { - const char* message = SendStart ? "start-sync\n" : "finish-sync\n"; + std::string message = SendStart ? "start-sync" : "finish-sync"; try { #ifdef WIN32 - mpCommandSocketInfo->mListeningSocket.Write(message, - (int)strlen(message)); + EnterCriticalSection(&mMessageQueueLock); + mMessageList.push_back(message); + SetEvent(mhMessageToSendEvent); + LeaveCriticalSection(&mMessageQueueLock); #else - mpCommandSocketInfo->mpConnectedSocket->Write(message, - strlen(message)); + message += "\n"; + mpCommandSocketInfo->mpConnectedSocket->Write( + message.c_str(), message.size()); #endif } catch(...) @@ -1756,39 +1877,36 @@ void BackupDaemon::SetState(int State) // command socket if there's an error char newState[64]; - char newStateSize = sprintf(newState, "state %d\n", State); + sprintf(newState, "state %d", State); + std::string message = newState; #ifdef WIN32 - #ifndef _MSC_VER - #warning FIX ME: race condition - #endif + EnterCriticalSection(&mMessageQueueLock); + mMessageList.push_back(newState); + SetEvent(mhMessageToSendEvent); + LeaveCriticalSection(&mMessageQueueLock); +#else + message += "\n"; - // 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()) + if(mpCommandSocketInfo == 0) { - try - { - mpCommandSocketInfo->mListeningSocket.Write(newState, newStateSize); - } - catch(...) - { - CloseCommandConnection(); - } + return; } -#else - if(mpCommandSocketInfo != 0 && mpCommandSocketInfo->mpConnectedSocket.get() != 0) + + if (mpCommandSocketInfo->mpConnectedSocket.get() == 0) { - // Something connected to the command socket, tell it about the new state - try - { - mpCommandSocketInfo->mpConnectedSocket->Write(newState, newStateSize); - } - catch(...) - { - CloseCommandConnection(); - } + return; + } + + // Something connected to the command socket, tell it about the new state + try + { + mpCommandSocketInfo->mpConnectedSocket->Write(message.c_str(), + message.length()); + } + catch(...) + { + CloseCommandConnection(); } #endif } @@ -2173,11 +2291,11 @@ static const int STOREOBJECTINFO_MAGIC_ID_VALUE = 0x7777525F; static const std::string STOREOBJECTINFO_MAGIC_ID_STRING = "BBACKUPD-STATE"; static const int STOREOBJECTINFO_VERSION = 1; -void BackupDaemon::SerializeStoreObjectInfo(int64_t aClientStoreMarker, box_time_t theLastSyncTime, box_time_t theNextSyncTime) const +bool BackupDaemon::SerializeStoreObjectInfo(int64_t aClientStoreMarker, box_time_t theLastSyncTime, box_time_t theNextSyncTime) const { if(!GetConfiguration().KeyExists("StoreObjectInfoFile")) { - return; + return false; } std::string StoreObjectInfoFile = @@ -2185,13 +2303,17 @@ void BackupDaemon::SerializeStoreObjectInfo(int64_t aClientStoreMarker, box_time if (StoreObjectInfoFile.size() <= 0) { - return; + return false; } + bool created = false; + try { FileStream aFile(StoreObjectInfoFile.c_str(), O_WRONLY | O_CREAT | O_TRUNC); + created = true; + Archive anArchive(aFile, 0); anArchive.Write(STOREOBJECTINFO_MAGIC_ID_VALUE); @@ -2236,6 +2358,8 @@ void BackupDaemon::SerializeStoreObjectInfo(int64_t aClientStoreMarker, box_time "not accessible or could not be created", StoreObjectInfoFile.c_str()); } + + return created; } // -------------------------------------------------------------------------- diff --git a/bin/bbackupd/BackupDaemon.h b/bin/bbackupd/BackupDaemon.h index 3bd15fad..94a142f5 100644 --- a/bin/bbackupd/BackupDaemon.h +++ b/bin/bbackupd/BackupDaemon.h @@ -46,9 +46,12 @@ public: ~BackupDaemon(); private: - // methods below do partial (specialized) serialization of client state only - void SerializeStoreObjectInfo(int64_t aClientStoreMarker, box_time_t theLastSyncTime, box_time_t theNextSyncTime) const; - bool DeserializeStoreObjectInfo(int64_t & aClientStoreMarker, box_time_t & theLastSyncTime, box_time_t & theNextSyncTime); + // methods below do partial (specialized) serialization of + // client state only + bool SerializeStoreObjectInfo(int64_t aClientStoreMarker, + box_time_t theLastSyncTime, box_time_t theNextSyncTime) const; + bool DeserializeStoreObjectInfo(int64_t & aClientStoreMarker, + box_time_t & theLastSyncTime, box_time_t & theNextSyncTime); bool DeleteStoreObjectInfo() const; BackupDaemon(const BackupDaemon &); public: @@ -182,7 +185,10 @@ private: void RunHelperThread(void); private: - bool mDoSyncFlagOut, mSyncIsForcedOut, mReceivedCommandConn; + bool mDoSyncFlagOut, mSyncIsForcedOut; + HANDLE mhMessageToSendEvent, mhCommandReceivedEvent; + CRITICAL_SECTION mMessageQueueLock; + std::vector<std::string> mMessageList; #endif }; diff --git a/bin/bbackupd/Win32BackupService.cpp b/bin/bbackupd/Win32BackupService.cpp index aa3bf55c..7cbf4828 100644 --- a/bin/bbackupd/Win32BackupService.cpp +++ b/bin/bbackupd/Win32BackupService.cpp @@ -12,40 +12,51 @@ #include "Win32BackupService.h" -Win32BackupService gDaemonService; +Win32BackupService* gpDaemonService = NULL; extern HANDLE gStopServiceEvent; unsigned int WINAPI RunService(LPVOID lpParameter) { - DWORD retVal = gDaemonService.WinService(); - SetEvent( gStopServiceEvent ); + DWORD retVal = gpDaemonService->WinService((const char*) lpParameter); + SetEvent(gStopServiceEvent); return retVal; } void TerminateService(void) { - gDaemonService.SetTerminateWanted(); + gpDaemonService->SetTerminateWanted(); } -DWORD Win32BackupService::WinService(void) +DWORD Win32BackupService::WinService(const char* pConfigFileName) { - int argc = 2; - //first off get the path name for the default - char buf[MAX_PATH]; - - GetModuleFileName(NULL, buf, sizeof(buf)); - std::string buffer(buf); - std::string conf( "-c"); - std::string cfile(buffer.substr(0,(buffer.find("bbackupd.exe"))) - + "bbackupd.conf"); + char exepath[MAX_PATH]; + GetModuleFileName(NULL, exepath, sizeof(exepath)); - const char *argv[] = {conf.c_str(), cfile.c_str()}; + std::string configfile; + + if (pConfigFileName != NULL) + { + configfile = pConfigFileName; + } + else + { + // make the default config file name, + // based on the program path + configfile = exepath; + configfile = configfile.substr(0, + configfile.rfind(DIRECTORY_SEPARATOR_ASCHAR)); + configfile += DIRECTORY_SEPARATOR "bbackupd.conf"; + } + + const char *argv[] = {exepath, "-c", configfile.c_str()}; + int argc = sizeof(argv) / sizeof(*argv); + DWORD ret; MAINHELPER_START - - return this->Main(BOX_FILE_BBACKUPD_DEFAULT_CONFIG, argc, argv); - + ret = this->Main(BOX_FILE_BBACKUPD_DEFAULT_CONFIG, argc, argv); MAINHELPER_END + + return ret; } #endif // WIN32 diff --git a/bin/bbackupd/Win32BackupService.h b/bin/bbackupd/Win32BackupService.h index 38cebacc..e7f077f2 100644 --- a/bin/bbackupd/Win32BackupService.h +++ b/bin/bbackupd/Win32BackupService.h @@ -12,7 +12,7 @@ class BackupDaemon; class Win32BackupService : public BackupDaemon { public: - DWORD WinService(void); + DWORD WinService(const char* pConfigFileName); }; #endif // WIN32 diff --git a/bin/bbackupd/Win32ServiceFunctions.cpp b/bin/bbackupd/Win32ServiceFunctions.cpp index b74c7e09..1b50a7fa 100644 --- a/bin/bbackupd/Win32ServiceFunctions.cpp +++ b/bin/bbackupd/Win32ServiceFunctions.cpp @@ -93,28 +93,30 @@ void WINAPI ServiceControlHandler( DWORD controlCode ) // It also returns on any error because the // service cannot start if there is an eror. +static char* spConfigFileName; + VOID ServiceMain(DWORD argc, LPTSTR *argv) { - // initialise service status - gServiceStatus.dwServiceType = SERVICE_WIN32; - gServiceStatus.dwCurrentState = SERVICE_STOPPED; - gServiceStatus.dwControlsAccepted = 0; - gServiceStatus.dwWin32ExitCode = NO_ERROR; - gServiceStatus.dwServiceSpecificExitCode = NO_ERROR; - gServiceStatus.dwCheckPoint = 0; - gServiceStatus.dwWaitHint = 0; - - gServiceStatusHandle = RegisterServiceCtrlHandler(gServiceName, - ServiceControlHandler); - - if (gServiceStatusHandle) - { - // service is starting - gServiceStatus.dwCurrentState = SERVICE_START_PENDING; - SetServiceStatus(gServiceStatusHandle, &gServiceStatus); - - // do initialisation here - gStopServiceEvent = CreateEvent( 0, TRUE, FALSE, 0 ); + // initialise service status + gServiceStatus.dwServiceType = SERVICE_WIN32; + gServiceStatus.dwCurrentState = SERVICE_STOPPED; + gServiceStatus.dwControlsAccepted = 0; + gServiceStatus.dwWin32ExitCode = NO_ERROR; + gServiceStatus.dwServiceSpecificExitCode = NO_ERROR; + gServiceStatus.dwCheckPoint = 0; + gServiceStatus.dwWaitHint = 0; + + gServiceStatusHandle = RegisterServiceCtrlHandler(gServiceName, + ServiceControlHandler); + + if (gServiceStatusHandle) + { + // service is starting + gServiceStatus.dwCurrentState = SERVICE_START_PENDING; + SetServiceStatus(gServiceStatusHandle, &gServiceStatus); + + // do initialisation here + gStopServiceEvent = CreateEvent(0, TRUE, FALSE, 0); if (!gStopServiceEvent) { gServiceStatus.dwControlsAccepted &= @@ -129,7 +131,7 @@ VOID ServiceMain(DWORD argc, LPTSTR *argv) NULL, 0, RunService, - 0, + spConfigFileName, CREATE_SUSPENDED, NULL); @@ -138,7 +140,7 @@ VOID ServiceMain(DWORD argc, LPTSTR *argv) // we are now running so tell the SCM gServiceStatus.dwControlsAccepted |= - (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN); + (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN); gServiceStatus.dwCurrentState = SERVICE_RUNNING; SetServiceStatus(gServiceStatusHandle, &gServiceStatus); @@ -156,11 +158,13 @@ VOID ServiceMain(DWORD argc, LPTSTR *argv) ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN); gServiceStatus.dwCurrentState = SERVICE_STOPPED; SetServiceStatus(gServiceStatusHandle, &gServiceStatus); - } + } } -void OurService(void) +void OurService(char* pConfigFileName) { + spConfigFileName = pConfigFileName; + SERVICE_TABLE_ENTRY serviceTable[] = { { SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) ServiceMain }, @@ -179,28 +183,52 @@ void OurService(void) } } -void InstallService(void) +int InstallService(const char* pConfigFileName) { - SC_HANDLE newService, scm; + if (pConfigFileName != NULL) + { + struct stat st; - scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE); + if (emu_stat(pConfigFileName, &st) != 0) + { + syslog(LOG_ERR, "Failed to open configuration file: " + "%s: %s", pConfigFileName, strerror(errno)); + return 1; + } + + if (! st.st_mode & S_IFREG) + { + + syslog(LOG_ERR, "Failed to open configuration file: " + "%s: not a file", pConfigFileName); + return 1; + } + } + + SC_HANDLE scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE); if (!scm) { syslog(LOG_ERR, "Failed to open service control manager: " "error %d", GetLastError()); - return; + return 1; } char cmd[MAX_PATH]; GetModuleFileName(NULL, cmd, sizeof(cmd)-1); cmd[sizeof(cmd)-1] = 0; - char cmd_args[MAX_PATH]; - _snprintf(cmd_args, sizeof(cmd_args)-1, "%s --service", cmd); - cmd_args[sizeof(cmd_args)-1] = 0; + std::string cmdWithArgs(cmd); + cmdWithArgs += " --service"; - newService = CreateService( + if (pConfigFileName != NULL) + { + cmdWithArgs += " \""; + cmdWithArgs += pConfigFileName; + cmdWithArgs += "\""; + } + + SC_HANDLE newService = CreateService( scm, SERVICE_NAME, "Box Backup", @@ -208,14 +236,36 @@ void InstallService(void) SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, - cmd_args, + cmdWithArgs.c_str(), 0,0,0,0,0); + DWORD err = GetLastError(); + CloseServiceHandle(scm); + if (!newService) { - ::syslog(LOG_ERR, "Failed to create Box Backup service: " - "error %d", GetLastError()); - return; + if (err == ERROR_SERVICE_EXISTS) + { + ::syslog(LOG_ERR, "Failed to create Box Backup " + "service: it already exists"); + } + else if (err == ERROR_SERVICE_MARKED_FOR_DELETE) + { + ::syslog(LOG_ERR, "Failed to create Box Backup " + "service: it is waiting to be deleted"); + } + else if (err == ERROR_DUPLICATE_SERVICE_NAME) + { + ::syslog(LOG_ERR, "Failed to create Box Backup " + "service: a service with this name " + "already exists"); + } + else + { + ::syslog(LOG_ERR, "Failed to create Box Backup " + "service: error %d", err); + } + return 1; } ::syslog(LOG_INFO, "Created Box Backup service"); @@ -231,45 +281,75 @@ void InstallService(void) } CloseServiceHandle(newService); - CloseServiceHandle(scm); + + return 0; } -void RemoveService(void) +int RemoveService(void) { - SC_HANDLE service, scm; - SERVICE_STATUS status; - - scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE); + SC_HANDLE scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE); if (!scm) { syslog(LOG_ERR, "Failed to open service control manager: " "error %d", GetLastError()); - return; + return 1; } - service = OpenService(scm, SERVICE_NAME, SERVICE_ALL_ACCESS|DELETE); - ControlService(service, SERVICE_CONTROL_STOP, &status); + SC_HANDLE service = OpenService(scm, SERVICE_NAME, + SERVICE_ALL_ACCESS|DELETE); + DWORD err = GetLastError(); + CloseServiceHandle(scm); if (!service) { - syslog(LOG_ERR, "Failed to open Box Backup service: " - "error %d", GetLastError()); - return; + if (err == ERROR_SERVICE_DOES_NOT_EXIST || + err == ERROR_IO_PENDING) + // hello microsoft? anyone home? + { + syslog(LOG_ERR, "Failed to open Box Backup service: " + "not installed or not found"); + } + else + { + syslog(LOG_ERR, "Failed to open Box Backup service: " + "error %d", err); + } + return 1; } - if (DeleteService(service)) + SERVICE_STATUS status; + if (!ControlService(service, SERVICE_CONTROL_STOP, &status)) + { + err = GetLastError(); + if (err != ERROR_SERVICE_NOT_ACTIVE) + { + syslog(LOG_WARNING, "Failed to stop Box Backup " + "service: error %d", err); + } + } + + BOOL deleted = DeleteService(service); + err = GetLastError(); + CloseServiceHandle(service); + + if (deleted) { syslog(LOG_INFO, "Box Backup service deleted"); + return 0; + } + else if (err == ERROR_SERVICE_MARKED_FOR_DELETE) + { + syslog(LOG_ERR, "Failed to remove Box Backup service: " + "it is already being deleted"); } else { syslog(LOG_ERR, "Failed to remove Box Backup service: " - "error %d", GetLastError()); + "error %d", err); } - CloseServiceHandle(service); - CloseServiceHandle(scm); + return 1; } #endif // WIN32 diff --git a/bin/bbackupd/Win32ServiceFunctions.h b/bin/bbackupd/Win32ServiceFunctions.h index 72f65479..3fe77187 100644 --- a/bin/bbackupd/Win32ServiceFunctions.h +++ b/bin/bbackupd/Win32ServiceFunctions.h @@ -12,8 +12,8 @@ #ifndef WIN32SERVICEFUNCTIONS_H #define WIN32SERVICEFUNCTIONS_H -void RemoveService(void); -void InstallService(void); -void OurService(void); +int RemoveService(void); +int InstallService(const char* pConfigFilePath); +void OurService(char* pConfigFileName); #endif diff --git a/bin/bbackupd/bbackupd.cpp b/bin/bbackupd/bbackupd.cpp index 089b2d09..e00d3628 100644 --- a/bin/bbackupd/bbackupd.cpp +++ b/bin/bbackupd/bbackupd.cpp @@ -19,7 +19,7 @@ #include "Win32ServiceFunctions.h" #include "Win32BackupService.h" - extern Win32BackupService gDaemonService; + extern Win32BackupService* gpDaemonService; #endif int main(int argc, const char *argv[]) @@ -28,7 +28,7 @@ int main(int argc, const char *argv[]) #ifdef WIN32 - ::openlog("Box Backup (bbackupd)", 0, 0); + ::openlog("Box Backup (bbackupd)", LOG_PID, LOG_LOCAL6); if(argc == 2 && (::strcmp(argv[1], "--help") == 0 || @@ -40,32 +40,25 @@ int main(int argc, const char *argv[]) } if(argc == 2 && ::strcmp(argv[1], "-r") == 0) { - RemoveService(); - return 0; + return RemoveService(); } - if(argc == 2 && ::strcmp(argv[1], "-i") == 0) + if((argc == 2 || argc == 3) && ::strcmp(argv[1], "-i") == 0) { - InstallService(); - return 0; + const char* config = NULL; + if (argc == 3) + { + config = argv[2]; + } + return InstallService(config); } bool runAsWin32Service = false; - if (argc == 2 && ::strcmp(argv[1], "--service") == 0) + if (argc >= 2 && ::strcmp(argv[1], "--service") == 0) { runAsWin32Service = true; } - - // Under win32 we must initialise the Winsock library - // before using sockets - - WSADATA info; - if (WSAStartup(0x0101, &info) == SOCKET_ERROR) - { - // box backup will not run without sockets - ::syslog(LOG_ERR, "Failed to initialise Windows Sockets"); - THROW_EXCEPTION(BackupStoreException, Internal) - } + gpDaemonService = new Win32BackupService(); EnableBackupRights(); @@ -73,20 +66,33 @@ int main(int argc, const char *argv[]) if (runAsWin32Service) { - syslog(LOG_INFO,"Starting Box Backup Service"); - OurService(); + syslog(LOG_INFO, "Box Backup service starting"); + + char* config = NULL; + if (argc >= 3) + { + config = strdup(argv[2]); + } + + OurService(config); + + if (config) + { + free(config); + } + + syslog(LOG_INFO, "Box Backup service shut down"); } else { - ExitCode = gDaemonService.Main( + ExitCode = gpDaemonService->Main( BOX_FILE_BBACKUPD_DEFAULT_CONFIG, argc, argv); } - // Clean up our sockets - WSACleanup(); - ::closelog(); + delete gpDaemonService; + return ExitCode; #else // !WIN32 |