[Box Backup-dev] COMMIT r232 - box/chris/boxi/bin/bbackupd

boxbackup-dev@fluffy.co.uk boxbackup-dev@fluffy.co.uk
Wed, 14 Dec 2005 23:54:13 +0000 (GMT)


Author: chris
Date: 2005-12-14 23:54:01 +0000 (Wed, 14 Dec 2005)
New Revision: 232

Modified:
   box/chris/boxi/bin/bbackupd/BackupClientContext.cpp
   box/chris/boxi/bin/bbackupd/BackupClientContext.h
   box/chris/boxi/bin/bbackupd/BackupClientDirectoryRecord.cpp
   box/chris/boxi/bin/bbackupd/BackupClientDirectoryRecord.h
   box/chris/boxi/bin/bbackupd/BackupDaemon.cpp
   box/chris/boxi/bin/bbackupd/BackupDaemon.h
Log:
* bbackupd/BackupDaemon.h
* bbackupd/BackupDaemon.cpp
- Moved command socket management (connect, wait, disconnect) to a separate
  class, CommandSocketManager, to make it accessible from other classes
  (win32 stuff should be moved as well, but not tonight)

* bbackupd/BackupClientContext.h
* bbackupd/BackupClientContext.cpp
- Use the LocationResolver interface to resolve paths to locations,
  making BackupClientContext independent of BackupDaemon

* bbackupd/BackupClientDirectoryRecord.h
* bbackupd/BackupClientDirectoryRecord.cpp
- Use RunStatusProvider, SysadminNotifier and ProgressNotifier to report
  backup progress, making BackupClientDirectoryRecord independent of 
  BackupDaemon
- Use CommandSocketManager to process command socket commands during a
  backup run, allowing a sync to be stopped midway by bbackupctl on Unixes
  (thread does this on win32)


Modified: box/chris/boxi/bin/bbackupd/BackupClientContext.cpp
===================================================================
--- box/chris/boxi/bin/bbackupd/BackupClientContext.cpp	2005-12-14 23:47:44 UTC (rev 231)
+++ box/chris/boxi/bin/bbackupd/BackupClientContext.cpp	2005-12-14 23:54:01 UTC (rev 232)
@@ -33,9 +33,10 @@
 //		Created: 2003/10/08
 //
 // --------------------------------------------------------------------------
-BackupClientContext::BackupClientContext(BackupDaemon &rDaemon, TLSContext &rTLSContext, const std::string &rHostname,
-			int32_t AccountNumber, bool ExtendedLogging)
-	: mrDaemon(rDaemon),
+BackupClientContext::BackupClientContext(LocationResolver &rResolver, 
+	TLSContext &rTLSContext, const std::string &rHostname,
+	int32_t AccountNumber, bool ExtendedLogging)
+	: mrResolver(rResolver),
 	  mrTLSContext(rTLSContext),
 	  mHostname(rHostname),
 	  mAccountNumber(AccountNumber),
@@ -424,7 +425,8 @@
 		{
 			// Location name -- look up in daemon's records
 			std::string locPath;
-			if(!mrDaemon.FindLocationPathName(elementName.GetClearFilename(), locPath))
+			if(!mrResolver.FindLocationPathName(elementName.GetClearFilename(), 
+				locPath))
 			{
 				// Didn't find the location... so can't give the local filename
 				return false;

Modified: box/chris/boxi/bin/bbackupd/BackupClientContext.h
===================================================================
--- box/chris/boxi/bin/bbackupd/BackupClientContext.h	2005-12-14 23:47:44 UTC (rev 231)
+++ box/chris/boxi/bin/bbackupd/BackupClientContext.h	2005-12-14 23:54:01 UTC (rev 232)
@@ -26,6 +26,23 @@
 // --------------------------------------------------------------------------
 //
 // Class
+//		Name:    LocationResolver
+//		Purpose: Interface for classes that can resolve locations to paths,
+//		         like BackupDaemon
+//		Created: 2003/10/08
+//
+// --------------------------------------------------------------------------
+class LocationResolver {
+        public:
+        virtual ~LocationResolver() { }
+		virtual bool FindLocationPathName(const std::string &rLocationName, 
+			std::string &rPathOut) const = 0;
+};
+
+
+// --------------------------------------------------------------------------
+//
+// Class
 //		Name:    BackupClientContext
 //		Purpose: 
 //		Created: 2003/10/08
@@ -34,8 +51,9 @@
 class BackupClientContext
 {
 public:
-	BackupClientContext(BackupDaemon &rDaemon, TLSContext &rTLSContext, const std::string &rHostname,
-		int32_t AccountNumber, bool ExtendedLogging);
+	BackupClientContext(LocationResolver &rResolver, TLSContext &rTLSContext, 
+		const std::string &rHostname, int32_t AccountNumber, 
+		bool ExtendedLogging);
 	~BackupClientContext();
 private:
 	BackupClientContext(const BackupClientContext &);
@@ -135,7 +153,7 @@
 		BackupStoreFilenameClear *pLeafname = 0); // not const as may connect to server
 
 private:
-	BackupDaemon &mrDaemon;
+	LocationResolver &mrResolver;
 	TLSContext &mrTLSContext;
 	std::string mHostname;
 	int32_t mAccountNumber;
@@ -153,4 +171,3 @@
 
 
 #endif // BACKUPCLIENTCONTEXT__H
-

Modified: box/chris/boxi/bin/bbackupd/BackupClientDirectoryRecord.cpp
===================================================================
--- box/chris/boxi/bin/bbackupd/BackupClientDirectoryRecord.cpp	2005-12-14 23:47:44 UTC (rev 231)
+++ box/chris/boxi/bin/bbackupd/BackupClientDirectoryRecord.cpp	2005-12-14 23:54:01 UTC (rev 232)
@@ -101,8 +101,14 @@
 void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::SyncParams &rParams, int64_t ContainingDirectoryID,
 	const std::string &rLocalPath, bool ThisDirHasJustBeenCreated)
 {
+	rParams.GetProgressNotifier().NotifyScanDirectory(this, rLocalPath);
+	
+	// Check for connections and commands on the command socket
+	if (rParams.mpCommandSocket)
+		rParams.mpCommandSocket->Wait(0);
+	
 	// Signal received by daemon?
-	if(rParams.mrDaemon.StopRun())
+	if(rParams.StopRun())
 	{
 		// Yes. Stop now.
 		THROW_EXCEPTION(BackupStoreException, SignalReceived)
@@ -133,8 +139,8 @@
 		{
 			// The directory has probably been deleted, so just ignore this error.
 			// In a future scan, this deletion will be noticed, deleted from server, and this object deleted.
-			TRACE1("Stat failed for '%s' (directory)\n", 
-				rLocalPath.c_str());
+			rParams.GetProgressNotifier().NotifyDirStatFailed(
+				this, rLocalPath, strerror(errno));
 			return;
 		}
 		// Store inode number in map so directories are tracked in case they're renamed
@@ -162,6 +168,7 @@
 	std::vector<std::string> dirs;
 	std::vector<std::string> files;
 	bool downloadDirectoryRecordBecauseOfFutureFiles = false;
+	
 	// BLOCK
 	{		
 		// read the contents...
@@ -210,6 +217,10 @@
 				{
 					// Report the error (logs and 
 					// eventual email to administrator)
+ 					rParams.GetProgressNotifier().NotifyFileStatFailed(this, 
+ 						filename, strerror(errno));
+					
+					// FIXME move to NotifyFileStatFailed()
 					SetErrorWhenReadingFilesystemObject(
 						rParams, filename.c_str());
 
@@ -268,8 +279,8 @@
 					// Log that this has happened
 					if(!rParams.mHaveLoggedWarningAboutFutureFileTimes)
 					{
-						::syslog(LOG_ERR, "Some files have modification times excessively in the future. Check clock syncronisation.\n");
-						::syslog(LOG_ERR, "Example file (only one shown) : %s\n", filename.c_str());
+						rParams.GetProgressNotifier().NotifyFileModifiedInFuture(
+							this, filename);
 						rParams.mHaveLoggedWarningAboutFutureFileTimes = true;
 					}
 				}
@@ -522,6 +533,8 @@
 			struct stat st;
 			if(::lstat(filename.c_str(), &st) != 0)
 			{
+				rParams.GetProgressNotifier().NotifyFileStatFailed(this, 
+					filename, strerror(errno));
 				THROW_EXCEPTION(CommonException, OSFileError)
 			}
 			
@@ -693,6 +706,8 @@
 				{
 					// Connection errors should just be passed on to the main handler, retries
 					// would probably just cause more problems.
+					rParams.GetProgressNotifier().NotifyFileUploadException(this,
+						filename, e);
 					throw;
 				}
 				catch(BoxException &e)
@@ -702,7 +717,8 @@
 					// Log it.
 					SetErrorWhenReadingFilesystemObject(rParams, filename.c_str());
 					// Log error.
-					::syslog(LOG_ERR, "Error code when uploading was (%d/%d), %s", e.GetType(), e.GetSubType(), e.what());
+					rParams.GetProgressNotifier().NotifyFileUploadException(this,
+						filename, e);
 				}
 
 				// Update structures if the file was uploaded successfully.
@@ -715,6 +731,11 @@
 					}
 				}
 			}
+			else
+			{
+				rParams.GetProgressNotifier().NotifyFileSkippedServerFull(this,
+					filename);
+			}
 		}
 		else if(en != 0 && en->GetAttributesHash() != attributesHash)
 		{
@@ -796,6 +817,9 @@
 				}
 			}
 		}
+		
+		rParams.GetProgressNotifier().NotifyFileSynchronised(this, filename, 
+			fileSize);
 	}
 
 	// Erase contents of files to save space when recursing
@@ -1070,6 +1094,8 @@
 int64_t BackupClientDirectoryRecord::UploadFile(BackupClientDirectoryRecord::SyncParams &rParams, const std::string &rFilename, const BackupStoreFilename &rStoreFilename,
 			int64_t FileSize, box_time_t ModificationTime, box_time_t AttributesHash, bool NoPreviousVersionOnServer)
 {
+	rParams.GetProgressNotifier().NotifyFileUploading(this, rFilename);
+	
 	// Get the connection
 	BackupProtocolClient &connection(rParams.mrContext.GetConnection());
 
@@ -1090,7 +1116,11 @@
 			
 			if(diffFromID != 0)
 			{
-				// Found an old version -- get the index
+				// Found an old version
+				rParams.GetProgressNotifier().NotifyFileUploadingPatch(this, 
+					rFilename);
+
+				// Get the index
 				std::auto_ptr<IOStream> blockIndexStream(connection.ReceiveStream());
 			
 				// Diff the file
@@ -1139,7 +1169,7 @@
 				&& subtype == BackupProtocolClientError::Err_StorageLimitExceeded)
 			{
 				// The hard limit was exceeded on the server, notify!
-				rParams.mrDaemon.NotifySysadmin(BackupDaemon::NotifyEvent_StoreFull);
+				rParams.NotifySysadmin(BackupDaemon::NotifyEvent_StoreFull);
 			}
 		}
 		
@@ -1147,6 +1177,8 @@
 		throw;
 	}
 
+	rParams.GetProgressNotifier().NotifyFileUploaded(this, rFilename, FileSize);
+
 	// Return the new object ID of this file
 	return objID;
 }
@@ -1167,7 +1199,8 @@
 	::memset(mStateChecksum, 0, sizeof(mStateChecksum));
 
 	// Log the error
-	::syslog(LOG_ERR, "Backup object failed, error when reading %s", Filename);
+	rParams.GetProgressNotifier().NotifyFileReadFailed(this, 
+		Filename, strerror(errno));
 
 	// Mark that an error occured in the parameters object
 	rParams.mReadErrorsOnFilesystemObjects = true;
@@ -1183,14 +1216,20 @@
 //		Created: 8/3/04
 //
 // --------------------------------------------------------------------------
-BackupClientDirectoryRecord::SyncParams::SyncParams(BackupDaemon &rDaemon, BackupClientContext &rContext)
-	: mSyncPeriodStart(0),
+BackupClientDirectoryRecord::SyncParams::SyncParams(
+	RunStatusProvider &rRunStatusProvider, 
+	SysadminNotifier &rSysadminNotifier,
+	ProgressNotifier &rProgressNotifier,
+	BackupClientContext &rContext)
+	: mrRunStatusProvider(rRunStatusProvider),
+	  mrSysadminNotifier(rSysadminNotifier),
+	  mrProgressNotifier(rProgressNotifier),
+	  mSyncPeriodStart(0),
 	  mSyncPeriodEnd(0),
 	  mMaxUploadWait(0),
 	  mMaxFileTimeInFuture(99999999999999999LL),
 	  mFileTrackingSizeThreshold(16*1024),
 	  mDiffingUploadSizeThreshold(16*1024),
-	  mrDaemon(rDaemon),
 	  mrContext(rContext),
 	  mReadErrorsOnFilesystemObjects(false),
 	  mUploadAfterThisTimeInTheFuture(99999999999999999LL),
@@ -1210,6 +1249,3 @@
 BackupClientDirectoryRecord::SyncParams::~SyncParams()
 {
 }
-
-
-

Modified: box/chris/boxi/bin/bbackupd/BackupClientDirectoryRecord.h
===================================================================
--- box/chris/boxi/bin/bbackupd/BackupClientDirectoryRecord.h	2005-12-14 23:47:44 UTC (rev 231)
+++ box/chris/boxi/bin/bbackupd/BackupClientDirectoryRecord.h	2005-12-14 23:54:01 UTC (rev 232)
@@ -20,10 +20,99 @@
 
 class BackupClientContext;
 class BackupDaemon;
+class CommandSocketManager;
 
+
 // --------------------------------------------------------------------------
 //
 // Class
+//		Name:    RunStatusProvider
+//		Purpose: Provides a StopRun() method which returns true if the current
+//		         backup should be halted.
+//		Created: 2005/11/15
+//
+// --------------------------------------------------------------------------
+class RunStatusProvider
+{
+	public:
+	virtual ~RunStatusProvider() { }
+	virtual bool StopRun() = 0;
+};
+
+// --------------------------------------------------------------------------
+//
+// Class
+//		Name:    SysadminNotifier
+//		Purpose: Provides a NotifySysadmin() method to send mail to the sysadmin
+//		Created: 2005/11/15
+//
+// --------------------------------------------------------------------------
+class SysadminNotifier
+{
+	public:
+	virtual ~SysadminNotifier() { }
+	virtual void NotifySysadmin(int Event) = 0;
+};
+
+// --------------------------------------------------------------------------
+//
+// Class
+//		Name:    ProgressNotifier
+//		Purpose: Provides methods for the backup library to inform the user
+//		         interface about its progress with the backup
+//		Created: 2005/11/20
+//
+// --------------------------------------------------------------------------
+class BackupClientDirectoryRecord;
+	
+class ProgressNotifier
+{
+	public:
+	virtual ~ProgressNotifier() { }
+	virtual void NotifyScanDirectory(
+		const BackupClientDirectoryRecord* pDirRecord,
+		const std::string& rLocalPath) = 0;
+	virtual void NotifyDirStatFailed(
+		const BackupClientDirectoryRecord* pDirRecord,
+		const std::string& rLocalPath,
+		const std::string& rErrorMsg) = 0;
+	virtual void NotifyFileStatFailed(
+		const BackupClientDirectoryRecord* pDirRecord,
+		const std::string& rLocalPath,
+		const std::string& rErrorMsg) = 0;
+	virtual void NotifyFileReadFailed(
+		const BackupClientDirectoryRecord* pDirRecord,
+		const std::string& rLocalPath,
+		const std::string& rErrorMsg) = 0;
+	virtual void NotifyFileModifiedInFuture(
+		const BackupClientDirectoryRecord* pDirRecord,
+		const std::string& rLocalPath) = 0;
+	virtual void NotifyFileSkippedServerFull(
+		const BackupClientDirectoryRecord* pDirRecord,
+		const std::string& rLocalPath) = 0;
+	virtual void NotifyFileUploadException(
+		const BackupClientDirectoryRecord* pDirRecord,
+		const std::string& rLocalPath,
+		const BoxException& rException) = 0;
+	virtual void NotifyFileUploading(
+		const BackupClientDirectoryRecord* pDirRecord,
+		const std::string& rLocalPath) = 0;
+	virtual void NotifyFileUploadingPatch(
+		const BackupClientDirectoryRecord* pDirRecord,
+		const std::string& rLocalPath) = 0;
+	virtual void NotifyFileUploaded(
+		const BackupClientDirectoryRecord* pDirRecord,
+		const std::string& rLocalPath,
+		int64_t FileSize) = 0;
+	virtual void NotifyFileSynchronised(
+		const BackupClientDirectoryRecord* pDirRecord,
+		const std::string& rLocalPath,
+		int64_t FileSize) = 0;
+};
+
+// --------------------------------------------------------------------------
+//
+// Class
 //		Name:    BackupClientDirectoryRecord
 //		Purpose: Implementation of record about directory for backup client
 //		Created: 2003/10/08
@@ -55,14 +144,21 @@
 	class SyncParams
 	{
 	public:
-		SyncParams(BackupDaemon &rDaemon, BackupClientContext &rContext);
+		SyncParams(
+			RunStatusProvider &rRunStatusProvider, 
+			SysadminNotifier &rSysadminNotifier,
+			ProgressNotifier &rProgressNotifier,
+			BackupClientContext &rContext);
 		~SyncParams();
 	private:
 		// No copying
 		SyncParams(const SyncParams&);
 		SyncParams &operator=(const SyncParams&);
+		RunStatusProvider &mrRunStatusProvider;
+		SysadminNotifier &mrSysadminNotifier;
+		ProgressNotifier &mrProgressNotifier;
+		
 	public:
-
 		// Data members are public, as accessors are not justified here
 		box_time_t mSyncPeriodStart;
 		box_time_t mSyncPeriodEnd;
@@ -70,13 +166,23 @@
 		box_time_t mMaxFileTimeInFuture;
 		int32_t mFileTrackingSizeThreshold;
 		int32_t mDiffingUploadSizeThreshold;
-		BackupDaemon &mrDaemon;
 		BackupClientContext &mrContext;
 		bool mReadErrorsOnFilesystemObjects;
-		
+		CommandSocketManager* mpCommandSocket;
+	
 		// Member variables modified by syncing process
 		box_time_t mUploadAfterThisTimeInTheFuture;
 		bool mHaveLoggedWarningAboutFutureFileTimes;
+	
+		bool StopRun() { return mrRunStatusProvider.StopRun(); }
+		void NotifySysadmin(int Event) 
+		{ 
+			mrSysadminNotifier.NotifySysadmin(Event); 
+		}
+		ProgressNotifier& GetProgressNotifier() const 
+		{ 
+			return mrProgressNotifier;
+		}
 	};
 
 	void SyncDirectory(SyncParams &rParams, int64_t ContainingDirectoryID, const std::string &rLocalPath,
@@ -111,5 +217,3 @@
 };
 
 #endif // BACKUPCLIENTDIRECTORYRECORD__H
-
-

Modified: box/chris/boxi/bin/bbackupd/BackupDaemon.cpp
===================================================================
--- box/chris/boxi/bin/bbackupd/BackupDaemon.cpp	2005-12-14 23:47:44 UTC (rev 231)
+++ box/chris/boxi/bin/bbackupd/BackupDaemon.cpp	2005-12-14 23:54:01 UTC (rev 232)
@@ -79,7 +79,7 @@
 //
 // --------------------------------------------------------------------------
 BackupDaemon::BackupDaemon()
-	: mState(BackupDaemon::State_Initialising),
+	: mState(State_Initialising),
 	  mpCommandSocketInfo(0),
 	  mDeleteUnusedRootDirEntriesAfter(0)
 {
@@ -223,7 +223,7 @@
 //		Created: 18/2/04
 //
 // --------------------------------------------------------------------------
-unsigned int WINAPI HelperThread( LPVOID lpParam ) 
+unsigned int cdecl HelperThread( LPVOID lpParam ) 
 { 
 	printf( "Parameter = %lu.\n", *(DWORD*)lpParam ); 
 	((BackupDaemon *)lpParam)->RunHelperThread();
@@ -285,15 +285,13 @@
 				else if(command == "sync")
 				{
 					// Sync now!
-					this->mDoSyncFlagOut = true;
-					this->mSyncIsForcedOut = false;
+					SetSyncRequested();
 					sendOK = true;
 				}
 				else if(command == "force-sync")
 				{
 					// Sync now (forced -- overrides any SyncAllowScript)
-					this->mDoSyncFlagOut = true;
-					this->mSyncIsForcedOut = true;
+					SetSyncForced();
 					sendOK = true;
 				}
 				else if(command == "reload")
@@ -377,10 +375,8 @@
 	if(conf.KeyExists("CommandSocket"))
 	{
 		// Yes, create a local UNIX socket
-		mpCommandSocketInfo = new CommandSocketInfo;
 		const char *socketName = conf.GetKeyValue("CommandSocket").c_str();
-		::unlink(socketName);
-		mpCommandSocketInfo->mListeningSocket.Listen(Socket::TypeUNIX, socketName);
+		mpCommandSocketInfo = new CommandSocketManager(conf, this, socketName);
 	}
 	
 #endif // WIN32
@@ -428,9 +424,10 @@
 	TLSContext tlsContext;
 	const Configuration &conf(GetConfiguration());
 	std::string certFile(conf.GetKeyValue("CertificateFile"));
-	std::string keyFile(conf.GetKeyValue("PrivateKeyFile"));
-	std::string caFile(conf.GetKeyValue("TrustedCAsFile"));
-	tlsContext.Initialise(false /* as client */, certFile.c_str(), keyFile.c_str(), caFile.c_str());
+	std::string keyFile (conf.GetKeyValue("PrivateKeyFile"));
+	std::string caFile  (conf.GetKeyValue("TrustedCAsFile"));
+	tlsContext.Initialise(false /* as client */, certFile.c_str(), 
+		keyFile.c_str(), caFile.c_str());
 	
 	// Set up the keys for various things
 	BackupClientCryptoKeys_Setup(conf.GetKeyValue("KeysFile").c_str());
@@ -476,8 +473,6 @@
 	{
 		// Flags used below
 		bool storageLimitExceeded = false;
-		bool doSync = false;
-		bool doSyncForcedByCommand = false;
 
 		// Is a delay necessary?
 		{
@@ -501,7 +496,8 @@
 					if(mpCommandSocketInfo != 0)
 					{
 						// A command socket exists, so sleep by handling connections with it
-						WaitOnCommandSocket(requiredDelay, doSync, doSyncForcedByCommand);
+						TRACE1("Wait on command socket, delay = %lld\n", requiredDelay);
+						mpCommandSocketInfo->Wait(requiredDelay);
 					}
 					else
 					{
@@ -511,36 +507,45 @@
 					}
 				}
 				
-			} while((!automaticBackup || (currentTime < nextSyncTime)) && !doSync && !StopRun());
+			} while((!automaticBackup || (currentTime < nextSyncTime)) 
+				&& !mSyncRequested && !mSyncForced && !StopRun());
 		}
 
 		// Time of sync start, and if it's time for another sync (and we're doing automatic syncs), set the flag
 		box_time_t currentSyncStartTime = GetCurrentBoxTime();
 		if(automaticBackup && currentSyncStartTime >= nextSyncTime)
 		{
-			doSync = true;
+			mSyncRequested = true;
 		}
 		
+		bool doSync = mSyncForced;
+		
 		// Use a script to see if sync is allowed now?
-		if(!doSyncForcedByCommand && doSync && !StopRun())
+		if(mSyncRequested && !StopRun())
 		{
 			int d = UseScriptToSeeIfSyncAllowed();
 			if(d > 0)
 			{
 				// Script has asked for a delay
 				nextSyncTime = GetCurrentBoxTime() + SecondsToBoxTime((uint32_t)d);
-				doSync = false;
 			}
+			else
+			{
+				doSync = true;
+			}
 		}
 
 		// Ready to sync? (but only if we're not supposed to be stopping)
 		if(doSync && !StopRun())
 		{
+			mSyncRequested = false;
+			mSyncForced    = false;
+			
 			// Touch a file to record times in filesystem
 			TouchFileInWorkingDir("last_sync_start");
 		
 			// Tell anything connected to the command socket
-			SendSyncStartOrFinish(true /* start */);
+			mpCommandSocketInfo->SendSyncStartOrFinish(true /* start */);
 			
 			// Reset statistics on uploads
 			BackupStoreFile::ResetStats();
@@ -581,13 +586,15 @@
 					conf.GetKeyValueInt("AccountNumber"), conf.GetKeyValueBool("ExtendedLogging"));
 					
 				// Set up the sync parameters
-				BackupClientDirectoryRecord::SyncParams params(*this, clientContext);
+				BackupClientDirectoryRecord::SyncParams params(*this, *this,
+					*this, clientContext);
 				params.mSyncPeriodStart = syncPeriodStart;
 				params.mSyncPeriodEnd = syncPeriodEndExtended; // use potentially extended end time
 				params.mMaxUploadWait = maxUploadWait;
 				params.mFileTrackingSizeThreshold = conf.GetKeyValueInt("FileTrackingSizeThreshold");
 				params.mDiffingUploadSizeThreshold = conf.GetKeyValueInt("DiffingUploadSizeThreshold");
 				params.mMaxFileTimeInFuture = SecondsToBoxTime((uint32_t)conf.GetKeyValueInt("MaxFileTimeInFuture"));
+				params.mpCommandSocket = mpCommandSocketInfo;
 				
 				// Set store marker
 				clientContext.SetClientStoreMarker(clientStoreMarker);
@@ -723,7 +730,22 @@
 						"to retry...", 
 						errorString, errorCode, 
 						errorSubCode);
-					::sleep(10);
+ 					
+ 					// Sleep somehow. There are choices on how this should be
+ 					// done, depending on the state of the control connection
+ 
+ 					if(mpCommandSocketInfo != 0)
+ 					{
+ 						// A command socket exists, so sleep by handling 
+						// connections with it
+ 						mpCommandSocketInfo->Wait(100 * 1000 * 1000);
+ 					}
+ 					else
+ 					{
+ 						// No command socket or connection, just do a 
+						// normal sleep
+ 						::sleep(100);
+ 					}
 				}
 			}
 
@@ -734,7 +756,7 @@
 			BackupStoreFile::ResetStats();
 
 			// Tell anything connected to the command socket
-			SendSyncStartOrFinish(false /* finish */);
+			mpCommandSocketInfo->SendSyncStartOrFinish(false /* finish */);
 
 			// Touch a file to record times in filesystem
 			TouchFileInWorkingDir("last_sync_finish");
@@ -820,288 +842,6 @@
 }
 
 
-
-// --------------------------------------------------------------------------
-//
-// Function
-//		Name:    BackupDaemon::WaitOnCommandSocket(box_time_t, bool &, bool &)
-//		Purpose: Waits on a the command socket for a time of UP TO the required time
-//				 but may be much less, and handles a command if necessary.
-//		Created: 18/2/04
-//
-// --------------------------------------------------------------------------
-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
-	
-	TRACE1("Wait on command socket, delay = %lld\n", RequiredDelay);
-	
-	try
-	{
-		// Timeout value for connections and things
-		int timeout = ((int)BoxTimeToMilliSeconds(RequiredDelay)) + 1;
-		// Handle bad boundary cases
-		if(timeout <= 0) timeout = 1;
-		if(timeout == INFTIM) timeout = 100000;
-
-		// Wait for socket connection, or handle a command?
-		if(mpCommandSocketInfo->mpConnectedSocket.get() == 0)
-		{
-			// No connection, listen for a new one
-			mpCommandSocketInfo->mpConnectedSocket.reset(mpCommandSocketInfo->mListeningSocket.Accept(timeout).release());
-			
-			if(mpCommandSocketInfo->mpConnectedSocket.get() == 0)
-			{
-				// If a connection didn't arrive, there was a timeout, which means we've
-				// waited long enough and it's time to go.
-				return;
-			}
-			else
-			{
-#ifdef PLATFORM_CANNOT_FIND_PEER_UID_OF_UNIX_SOCKET
-				bool uidOK = true;
-				::syslog(LOG_ERR, "On this platform, no security check can be made on the credientials 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?
-				bool uidOK = false;
-				// BLOCK
-				{
-					uid_t remoteEUID = 0xffff;
-					gid_t remoteEGID = 0xffff;
-					if(mpCommandSocketInfo->mpConnectedSocket->GetPeerCredentials(remoteEUID, remoteEGID))
-					{
-						// Credentials are available -- check UID
-						if(remoteEUID == ::getuid())
-						{
-							// Acceptable
-							uidOK = true;
-						}
-					}
-				}
-#endif // PLATFORM_CANNOT_FIND_PEER_UID_OF_UNIX_SOCKET
-				
-				// Is this an acceptable connection?
-				if(!uidOK)
-				{
-					// Dump the connection
-					::syslog(LOG_ERR, "Incoming command connection from peer had different user ID than this process, or security check could not be completed.");
-					mpCommandSocketInfo->mpConnectedSocket.reset();
-					return;
-				}
-				else
-				{
-					// 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];
-					int 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->mpConnectedSocket->Write(summary, summarySize);
-					
-					// Set the timeout to something very small, so we don't wait too long on waiting
-					// for any incoming data
-					timeout = 10; // milliseconds
-				}
-			}
-		}
-
-		// So there must be a connection now.
-		ASSERT(mpCommandSocketInfo->mpConnectedSocket.get() != 0);
-		
-		// Is there a getline object ready?
-		if(mpCommandSocketInfo->mpGetLine == 0)
-		{
-			// Create a new one
-			mpCommandSocketInfo->mpGetLine = new IOStreamGetLine(*(mpCommandSocketInfo->mpConnectedSocket.get()));
-		}
-		
-		// Ping the remote side, to provide errors which will mean the socket gets closed
-		mpCommandSocketInfo->mpConnectedSocket->Write("ping\n", 5);
-		
-		// Wait for a command or something on the socket
-		std::string command;
-		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());
-			
-			bool sendOK = false;
-			bool sendResponse = true;
-		
-			// Command to process!
-			if(command == "quit" || command == "")
-			{
-				// Close the socket.
-				CloseCommandConnection();
-				sendResponse = false;
-			}
-			else if(command == "sync")
-			{
-				// Sync now!
-				DoSyncFlagOut = true;
-				SyncIsForcedOut = false;
-				sendOK = true;
-			}
-			else if(command == "force-sync")
-			{
-				// Sync now (forced -- overrides any SyncAllowScript)
-				DoSyncFlagOut = true;
-				SyncIsForcedOut = 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)
-			{
-				mpCommandSocketInfo->mpConnectedSocket->Write(sendOK?"ok\n":"error\n", sendOK?3:6);
-			}
-			
-			// Set timeout to something very small, so this just checks for data which is waiting
-			timeout = 1;
-		}
-		
-		// Close on EOF?
-		if(mpCommandSocketInfo->mpGetLine != 0 && mpCommandSocketInfo->mpGetLine->IsEOF())
-		{
-			CloseCommandConnection();
-		}
-	}
-	catch(...)
-	{
-		// If an error occurs, and there is a connection active, just close that
-		// connection and continue. Otherwise, let the error propagate.
-		if(mpCommandSocketInfo->mpConnectedSocket.get() == 0)
-		{
-			throw;
-		}
-		else
-		{
-			// Close socket and ignore error
-			CloseCommandConnection();
-		}
-	}
-#endif // WIN32
-}
-
-
-// --------------------------------------------------------------------------
-//
-// Function
-//		Name:    BackupDaemon::CloseCommandConnection()
-//		Purpose: Close the command connection, ignoring any errors
-//		Created: 18/2/04
-//
-// --------------------------------------------------------------------------
-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(...)
-	{
-		// Ignore any errors
-	}
-}
-
-
-// --------------------------------------------------------------------------
-//
-// File
-//		Name:    BackupDaemon.cpp
-//		Purpose: Send a start or finish sync message to the command socket, if it's connected.
-//				 
-//		Created: 18/2/04
-//
-// --------------------------------------------------------------------------
-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 != NULL &&
-#ifdef WIN32
-	    mpCommandSocketInfo->mListeningSocket.IsConnected()
-#else
-	    mpCommandSocketInfo->mpConnectedSocket.get() != 0
-#endif
-	    )
-	{
-		const char* message = SendStart ? "start-sync\n" : "finish-sync\n";
-		try
-		{
-#ifdef WIN32
-			mpCommandSocketInfo->mListeningSocket.Write(message, 
-				strlen(message));
-#else
-			mpCommandSocketInfo->mpConnectedSocket->Write(message,
-				strlen(message));
-#endif
-		}
-		catch(...)
-		{
-			CloseCommandConnection();
-		}
-	}
-}
-
-
-
-
 #ifndef HAVE_STRUCT_STATFS_F_MNTONNAME
 	// string comparison ordering for when mount points are handled
 	// by code, rather than the OS.
@@ -1663,7 +1403,7 @@
 //		Created: 11/12/03
 //
 // --------------------------------------------------------------------------
-void BackupDaemon::SetState(int State)
+void BackupDaemon::SetState(state_t State)
 {
 	// Two little checks
 	if(State == mState) return;
@@ -1679,6 +1419,11 @@
 	// If there's a command socket connected, then inform it -- disconnecting from the
 	// command socket if there's an error
 
+ 	if (mpCommandSocketInfo != 0)
+  	{
+ 		mpCommandSocketInfo->SendStateUpdate(mState);
+	}
+
 	char newState[64];
 	char newStateSize = sprintf(newState, "state %d\n", State);
 
@@ -1864,35 +1609,3 @@
 		mpExcludeFiles = 0;
 	}
 }
-
-
-// --------------------------------------------------------------------------
-//
-// Function
-//		Name:    BackupDaemon::CommandSocketInfo::CommandSocketInfo()
-//		Purpose: Constructor
-//		Created: 18/2/04
-//
-// --------------------------------------------------------------------------
-BackupDaemon::CommandSocketInfo::CommandSocketInfo()
-	: mpGetLine(0)
-{
-}
-
-
-// --------------------------------------------------------------------------
-//
-// Function
-//		Name:    BackupDaemon::CommandSocketInfo::~CommandSocketInfo()
-//		Purpose: Destructor
-//		Created: 18/2/04
-//
-// --------------------------------------------------------------------------
-BackupDaemon::CommandSocketInfo::~CommandSocketInfo()
-{
-	if(mpGetLine)
-	{
-		delete mpGetLine;
-		mpGetLine = 0;
-	}
-}

Modified: box/chris/boxi/bin/bbackupd/BackupDaemon.h
===================================================================
--- box/chris/boxi/bin/bbackupd/BackupDaemon.h	2005-12-14 23:47:44 UTC (rev 231)
+++ box/chris/boxi/bin/bbackupd/BackupDaemon.h	2005-12-14 23:54:01 UTC (rev 232)
@@ -15,18 +15,13 @@
 #include <memory>
 
 #include "Daemon.h"
-#include "BoxTime.h"
-#include "Socket.h"
-#include "SocketListen.h"
-#include "SocketStream.h"
-#include "WinNamedPipeStream.h"
+#include "CommandSocketManager.h"
+#include "BackupClientContext.h"
+#include "BackupClientDirectoryRecord.h"
 
-class BackupClientDirectoryRecord;
-class BackupClientContext;
 class Configuration;
 class BackupClientInodeToIDMap;
 class ExcludeList;
-class IOStreamGetLine;
 
 // --------------------------------------------------------------------------
 //
@@ -36,7 +31,8 @@
 //		Created: 2003/10/08
 //
 // --------------------------------------------------------------------------
-class BackupDaemon : public Daemon
+class BackupDaemon : public Daemon, CommandListener, LocationResolver,
+	RunStatusProvider, SysadminNotifier, ProgressNotifier
 {
 public:
 	BackupDaemon();
@@ -50,20 +46,11 @@
 	virtual const char *DaemonBanner() const;
 	const ConfigurationVerify *GetConfigVerify() const;
 
-	bool FindLocationPathName(const std::string &rLocationName, std::string &rPathOut) const;
+	bool FindLocationPathName(const std::string &rLocationName, 
+		std::string &rPathOut) const;
 
-	enum
-	{
-		// Add stuff to this, make sure the textual equivalents in SetState() are changed too.
-		State_Initialising = -1,
-		State_Idle = 0,
-		State_Connected = 1,
-		State_Error = 2,
-		State_StorageLimitExceeded = 3
-	};
+	state_t GetState() {return mState;}
 
-	int GetState() {return mState;}
-
 	// Allow other classes to call this too
 	enum
 	{
@@ -94,12 +81,8 @@
 	
 	void MakeMapBaseName(unsigned int MountNumber, std::string &rNameOut) const;
 
-	void SetState(int State);
+	void SetState(state_t State);
 	
-	void WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFlagOut, bool &SyncIsForcedOut);
-	void CloseCommandConnection();
-	void SendSyncStartOrFinish(bool SendStart);
-	
 	void TouchFileInWorkingDir(const char *Filename);
 
 	void DeleteUnusedRootDirEntries(BackupClientContext &rContext);
@@ -111,7 +94,6 @@
 
 	int UseScriptToSeeIfSyncAllowed();
 
-private:
 	class Location
 	{
 	public:
@@ -129,7 +111,7 @@
 		ExcludeList *mpExcludeDirs;
 	};
 
-	int mState;		// what the daemon is currently doing
+	state_t mState; // what the daemon is currently doing
 
 	std::vector<Location *> mLocations;
 	
@@ -137,28 +119,9 @@
 	std::vector<BackupClientInodeToIDMap *> mCurrentIDMaps;
 	std::vector<BackupClientInodeToIDMap *> mNewIDMaps;
 	
-	// For the command socket
-	class CommandSocketInfo
-	{
-	public:
-		CommandSocketInfo();
-		~CommandSocketInfo();
-	private:
-		CommandSocketInfo(const CommandSocketInfo &);	// no copying
-		CommandSocketInfo &operator=(const CommandSocketInfo &);
-	public:
-#ifdef WIN32
-		WinNamedPipeStream mListeningSocket;
-#else
-		SocketListen<SocketStream, 1 /* listen backlog */> mListeningSocket;
-		std::auto_ptr<SocketStream> mpConnectedSocket;
-#endif
-		IOStreamGetLine *mpGetLine;
-	};
-	
-	// Using a socket?
-	CommandSocketInfo *mpCommandSocketInfo;
-	
+  	// Using a socket?
+ 	CommandSocketManager *mpCommandSocketInfo;
+
 	// Stop notifications being repeated.
 	bool mNotificationsSent[NotifyEvent__MAX + 1];
 
@@ -166,12 +129,81 @@
 	box_time_t mDeleteUnusedRootDirEntriesAfter;	// time to delete them
 	std::vector<std::pair<int64_t,std::string> > mUnusedRootDirEntries;
 
+ 	bool mSyncRequested;
+ 	bool mSyncForced;
+ 
+ 	void SetReloadConfigWanted() { this->Daemon::SetReloadConfigWanted(); }
+ 	void SetTerminateWanted()    { this->Daemon::SetTerminateWanted(); }
+ 	void SetSyncRequested()      { mSyncRequested = true; }
+ 	void SetSyncForced()         { mSyncForced    = true; }
+ 	
+ 	bool StopRun() { return this->Daemon::StopRun(); }
+ 
+ 	/* ProgressNotifier implementation */
+ 	virtual void NotifyScanDirectory(
+ 		const BackupClientDirectoryRecord* pDirRecord,
+ 		const std::string& rLocalPath) { }
+ 	virtual void NotifyDirStatFailed(
+ 		const BackupClientDirectoryRecord* pDirRecord,
+ 		const std::string& rLocalPath, 
+ 		const std::string& rErrorMsg)
+ 	{
+ 		TRACE2("Stat failed for '%s' (directory): %s\n", 
+ 			rLocalPath.c_str(), rErrorMsg.c_str());
+ 	}
+ 	virtual void NotifyFileStatFailed(
+ 		const BackupClientDirectoryRecord* pDirRecord,
+ 		const std::string& rLocalPath,
+ 		const std::string& rErrorMsg)
+ 	{
+ 		TRACE1("Stat failed for '%s' (contents)\n", rLocalPath.c_str());
+ 	}
+ 	virtual void NotifyFileReadFailed(
+ 		const BackupClientDirectoryRecord* pDirRecord,
+ 		const std::string& rLocalPath,
+ 		const std::string& rErrorMsg)
+ 	{
+ 		::syslog(LOG_ERR, "Backup object failed, error when reading %s", 
+ 			rLocalPath.c_str());
+ 	}
+ 	virtual void NotifyFileModifiedInFuture(
+ 		const BackupClientDirectoryRecord* pDirRecord,
+ 		const std::string& rLocalPath)
+ 	{
+ 		::syslog(LOG_ERR, "Some files have modification times "
+ 			"excessively in the future. Check clock syncronisation.\n");
+ 		::syslog(LOG_ERR, "Example file (only one shown) : %s\n", 
+ 			rLocalPath.c_str());
+ 	}
+ 	virtual void NotifyFileSkippedServerFull(
+ 		const BackupClientDirectoryRecord* pDirRecord,
+ 		const std::string& rLocalPath) { }
+ 	virtual void NotifyFileUploadException(
+ 		const BackupClientDirectoryRecord* pDirRecord,
+ 		const std::string& rLocalPath,
+ 		const BoxException& rException)
+ 	{
+ 		::syslog(LOG_ERR, "Error code when uploading was (%d/%d), %s", 
+			rException.GetType(), rException.GetSubType(), rException.what());
+ 	}
+ 	virtual void NotifyFileUploading(
+ 		const BackupClientDirectoryRecord* pDirRecord,
+ 		const std::string& rLocalPath) { }
+ 	virtual void NotifyFileUploadingPatch(
+ 		const BackupClientDirectoryRecord* pDirRecord,
+ 		const std::string& rLocalPath) { }
+ 	virtual void NotifyFileUploaded(
+ 		const BackupClientDirectoryRecord* pDirRecord,
+ 		const std::string& rLocalPath,
+ 		int64_t FileSize) { }
+ 	virtual void NotifyFileSynchronised(
+ 		const BackupClientDirectoryRecord* pDirRecord,
+ 		const std::string& rLocalPath,
+ 		int64_t FileSize) { }
+
 #ifdef WIN32
 	public:
 	void RunHelperThread(void);
-
-	private:
-	bool mDoSyncFlagOut, mSyncIsForcedOut, mReceivedCommandConn;
 #endif
 };