summaryrefslogtreecommitdiff
path: root/bin/bbackupd
diff options
context:
space:
mode:
authorMartin Ebourne <martin@ebourne.me.uk>2005-12-12 20:50:00 +0000
committerMartin Ebourne <martin@ebourne.me.uk>2005-12-12 20:50:00 +0000
commit3bedf8846f4d7a5cb38276b274662d62a36dcd52 (patch)
tree9d51de8b0f3d06ba6549a5a1958e52f592343140 /bin/bbackupd
parent81d8eda2419e7a23088a98cdfc52a305c9ceac0d (diff)
Marged chris/win32/merge/07-win32-fixes at r210 to trunk
Diffstat (limited to 'bin/bbackupd')
-rwxr-xr-xbin/bbackupd/BackupClientContext.cpp2
-rwxr-xr-xbin/bbackupd/BackupClientDirectoryRecord.cpp30
-rwxr-xr-xbin/bbackupd/BackupClientInodeToIDMap.cpp51
-rwxr-xr-xbin/bbackupd/BackupClientInodeToIDMap.h12
-rwxr-xr-xbin/bbackupd/BackupDaemon.cpp291
-rwxr-xr-xbin/bbackupd/BackupDaemon.h14
-rw-r--r--bin/bbackupd/Win32BackupService.cpp51
-rw-r--r--bin/bbackupd/Win32BackupService.h21
-rw-r--r--bin/bbackupd/Win32ServiceFunctions.cpp236
-rw-r--r--bin/bbackupd/Win32ServiceFunctions.h19
-rwxr-xr-xbin/bbackupd/bbackupd.cpp72
-rw-r--r--bin/bbackupd/win32/ReadMe.txt24
-rw-r--r--bin/bbackupd/win32/bbackupd.conf143
-rw-r--r--bin/bbackupd/win32/installer.iss51
14 files changed, 974 insertions, 43 deletions
diff --git a/bin/bbackupd/BackupClientContext.cpp b/bin/bbackupd/BackupClientContext.cpp
index 08a203c1..50f2cd08 100755
--- a/bin/bbackupd/BackupClientContext.cpp
+++ b/bin/bbackupd/BackupClientContext.cpp
@@ -9,7 +9,9 @@
#include "Box.h"
+#ifndef WIN32
#include <syslog.h>
+#endif
#include "BoxPortsAndFiles.h"
#include "BoxTime.h"
diff --git a/bin/bbackupd/BackupClientDirectoryRecord.cpp b/bin/bbackupd/BackupClientDirectoryRecord.cpp
index 307c72c4..1a124333 100755
--- a/bin/bbackupd/BackupClientDirectoryRecord.cpp
+++ b/bin/bbackupd/BackupClientDirectoryRecord.cpp
@@ -133,7 +133,8 @@ void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::Syn
{
// 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());
+ TRACE1("Stat failed for '%s' (directory)\n",
+ rLocalPath.c_str());
return;
}
// Store inode number in map so directories are tracked in case they're renamed
@@ -202,11 +203,18 @@ void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::Syn
}
// Stat file to get info
- filename = rLocalPath + DIRECTORY_SEPARATOR + en->d_name;
+ filename = rLocalPath + DIRECTORY_SEPARATOR +
+ en->d_name;
+
if(::lstat(filename.c_str(), &st) != 0)
{
- TRACE1("Stat failed for '%s' (contents)\n", filename.c_str());
- THROW_EXCEPTION(CommonException, OSFileError)
+ // Report the error (logs and
+ // eventual email to administrator)
+ SetErrorWhenReadingFilesystemObject(
+ rParams, filename.c_str());
+
+ // Ignore this entry for now.
+ continue;
}
int type = st.st_mode & S_IFMT;
@@ -506,7 +514,7 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
box_time_t modTime = 0;
uint64_t attributesHash = 0;
int64_t fileSize = 0;
- ino_t inodeNum = 0;
+ InodeRefType inodeNum = 0;
bool hasMultipleHardLinks = true;
// BLOCK
{
@@ -865,7 +873,7 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
// Get attributes
box_time_t attrModTime = 0;
- ino_t inodeNum = 0;
+ InodeRefType inodeNum = 0;
BackupClientFileAttributes attr;
attr.ReadAttributes(dirname.c_str(), true /* directories have zero mod times */,
0 /* not interested in mod time */, &attrModTime, 0 /* not file size */,
@@ -1109,8 +1117,12 @@ int64_t BackupClientDirectoryRecord::UploadFile(BackupClientDirectoryRecord::Syn
std::auto_ptr<IOStream> upload(BackupStoreFile::EncodeFile(rFilename.c_str(), mObjectID, rStoreFilename));
// Send to store
- std::auto_ptr<BackupProtocolClientSuccess> stored(connection.QueryStoreFile(mObjectID, ModificationTime,
- AttributesHash, 0 /* no diff from file ID */, rStoreFilename, *upload));
+ std::auto_ptr<BackupProtocolClientSuccess> stored(
+ connection.QueryStoreFile(
+ mObjectID, ModificationTime,
+ AttributesHash,
+ 0 /* no diff from file ID */,
+ rStoreFilename, *upload));
// Get object ID from the result
objID = stored->GetObjectID();
@@ -1130,7 +1142,7 @@ int64_t BackupClientDirectoryRecord::UploadFile(BackupClientDirectoryRecord::Syn
rParams.mrDaemon.NotifySysadmin(BackupDaemon::NotifyEvent_StoreFull);
}
}
-
+
// Send the error on it's way
throw;
}
diff --git a/bin/bbackupd/BackupClientInodeToIDMap.cpp b/bin/bbackupd/BackupClientInodeToIDMap.cpp
index 7141d5f4..c3f20df0 100755
--- a/bin/bbackupd/BackupClientInodeToIDMap.cpp
+++ b/bin/bbackupd/BackupClientInodeToIDMap.cpp
@@ -65,7 +65,11 @@ BackupClientInodeToIDMap::~BackupClientInodeToIDMap()
#ifndef BACKIPCLIENTINODETOIDMAP_IN_MEMORY_IMPLEMENTATION
if(dbp != 0)
{
+#ifdef BERKELY_V4
+ dbp->close(0);
+#else
dbp->close(dbp);
+#endif
}
#endif
}
@@ -90,7 +94,14 @@ void BackupClientInodeToIDMap::Open(const char *Filename, bool ReadOnly, bool Cr
ASSERT(!mEmpty);
// Open the database file
+#ifdef BERKELY_V4
+ dbp = new Db(0,0);
+ dbp->set_pagesize(1024); /* Page size: 1K. */
+ dbp->set_cachesize(0, 32 * 1024, 0);
+ dbp->open(NULL, Filename, NULL, DB_HASH, DB_CREATE, 0664);
+#else
dbp = dbopen(Filename, (CreateNew?O_CREAT:0) | (ReadOnly?O_RDONLY:O_RDWR), S_IRUSR | S_IWUSR | S_IRGRP, TABLE_DATABASE_TYPE, NULL);
+#endif
if(dbp == NULL)
{
THROW_EXCEPTION(BackupStoreException, BerkelyDBFailure);
@@ -135,7 +146,11 @@ void BackupClientInodeToIDMap::Close()
#ifndef BACKIPCLIENTINODETOIDMAP_IN_MEMORY_IMPLEMENTATION
if(dbp != 0)
{
+#ifdef BERKELY_V4
+ if(dbp->close(0) != 0)
+#else
if(dbp->close(dbp) != 0)
+#endif
{
THROW_EXCEPTION(BackupStoreException, BerkelyDBFailure);
}
@@ -172,6 +187,15 @@ void BackupClientInodeToIDMap::AddToMap(InodeRefType InodeRef, int64_t ObjectID,
IDBRecord rec;
rec.mObjectID = ObjectID;
rec.mInDirectory = InDirectory;
+
+#ifdef BERKELY_V4
+ Dbt key(&InodeRef, sizeof(InodeRef));
+ Dbt data(&rec, sizeof(rec));
+
+ if (dbp->put(0, &key, &data, 0) != 0) {
+ THROW_EXCEPTION(BackupStoreException, BerkelyDBFailure);
+ }
+#else
DBT key;
key.data = &InodeRef;
@@ -187,6 +211,7 @@ void BackupClientInodeToIDMap::AddToMap(InodeRefType InodeRef, int64_t ObjectID,
THROW_EXCEPTION(BackupStoreException, BerkelyDBFailure);
}
#endif
+#endif
}
// --------------------------------------------------------------------------
@@ -225,6 +250,11 @@ bool BackupClientInodeToIDMap::Lookup(InodeRefType InodeRef, int64_t &rObjectIDO
THROW_EXCEPTION(BackupStoreException, InodeMapNotOpen);
}
+#ifdef BERKELY_V4
+ Dbt key(&InodeRef, sizeof(InodeRef));
+ Dbt data(0, 0);
+ switch(dbp->get(NULL, &key, &data, 0))
+#else
DBT key;
key.data = &InodeRef;
key.size = sizeof(InodeRef);
@@ -234,6 +264,8 @@ bool BackupClientInodeToIDMap::Lookup(InodeRefType InodeRef, int64_t &rObjectIDO
data.size = 0;
switch(dbp->get(dbp, &key, &data, 0))
+#endif
+
{
case 1: // key not in file
return false;
@@ -248,6 +280,21 @@ bool BackupClientInodeToIDMap::Lookup(InodeRefType InodeRef, int64_t &rObjectIDO
}
// Check for sensible return
+#ifdef BERKELY_V4
+ if(key.get_data() == 0 || data.get_size() != sizeof(IDBRecord))
+ {
+ // Assert in debug version
+ ASSERT(key.get_data() == 0 || data.get_size() != sizeof(IDBRecord));
+
+ // Invalid entries mean it wasn't found
+ return false;
+ }
+
+ // Data alignment isn't guaranteed to be on a suitable boundary
+ IDBRecord rec;
+
+ ::memcpy(&rec, data.get_data(), sizeof(rec));
+#else
if(key.data == 0 || data.size != sizeof(IDBRecord))
{
// Assert in debug version
@@ -257,9 +304,11 @@ bool BackupClientInodeToIDMap::Lookup(InodeRefType InodeRef, int64_t &rObjectIDO
return false;
}
- // Data alignment isn't guarentted to be on a suitable bounday
+ // Data alignment isn't guaranteed to be on a suitable boundary
IDBRecord rec;
+
::memcpy(&rec, data.data, sizeof(rec));
+#endif
// Return data
rObjectIDOut = rec.mObjectID;
diff --git a/bin/bbackupd/BackupClientInodeToIDMap.h b/bin/bbackupd/BackupClientInodeToIDMap.h
index 121e88fd..1dfef702 100755
--- a/bin/bbackupd/BackupClientInodeToIDMap.h
+++ b/bin/bbackupd/BackupClientInodeToIDMap.h
@@ -20,12 +20,14 @@
#define BACKIPCLIENTINODETOIDMAP_IN_MEMORY_IMPLEMENTATION
#endif
-typedef ino_t InodeRefType;
-
// avoid having to include the DB files when not necessary
#ifndef BACKIPCLIENTINODETOIDMAP_IMPLEMENTATION
+#ifdef BERKELY_V4
+ class Db;
+#else
class DB;
#endif
+#endif
// --------------------------------------------------------------------------
//
@@ -58,8 +60,12 @@ private:
#else
bool mReadOnly;
bool mEmpty;
+#ifdef BERKELY_V4
+ Db *dbp; // c++ style implimentation
+#else
DB *dbp; // C style interface, use notation from documentation
-#endif
+#endif // BERKELY_V4
+#endif // BACKIPCLIENTINODETOIDMAP_IN_MEMORY_IMPLEMENTATION
};
#endif // BACKUPCLIENTINODETOIDMAP__H
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;
}
}
-
diff --git a/bin/bbackupd/BackupDaemon.h b/bin/bbackupd/BackupDaemon.h
index ffaf5783..e6798798 100755
--- a/bin/bbackupd/BackupDaemon.h
+++ b/bin/bbackupd/BackupDaemon.h
@@ -19,6 +19,7 @@
#include "Socket.h"
#include "SocketListen.h"
#include "SocketStream.h"
+#include "WinNamedPipeStream.h"
class BackupClientDirectoryRecord;
class BackupClientContext;
@@ -146,8 +147,12 @@ 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;
};
@@ -160,7 +165,14 @@ private:
// Unused entries in the root directory wait a while before being deleted
box_time_t mDeleteUnusedRootDirEntriesAfter; // time to delete them
std::vector<std::pair<int64_t,std::string> > mUnusedRootDirEntries;
+
+#ifdef WIN32
+ public:
+ void RunHelperThread(void);
+
+ private:
+ bool mDoSyncFlagOut, mSyncIsForcedOut, mReceivedCommandConn;
+#endif
};
#endif // BACKUPDAEMON__H
-
diff --git a/bin/bbackupd/Win32BackupService.cpp b/bin/bbackupd/Win32BackupService.cpp
new file mode 100644
index 00000000..aa3bf55c
--- /dev/null
+++ b/bin/bbackupd/Win32BackupService.cpp
@@ -0,0 +1,51 @@
+// Win32 service functions for Box Backup, by Nick Knight
+
+#ifdef WIN32
+
+#include "Box.h"
+#include "BackupDaemon.h"
+#include "MainHelper.h"
+#include "BoxPortsAndFiles.h"
+#include "BackupStoreException.h"
+
+#include "MemLeakFindOn.h"
+
+#include "Win32BackupService.h"
+
+Win32BackupService gDaemonService;
+extern HANDLE gStopServiceEvent;
+
+unsigned int WINAPI RunService(LPVOID lpParameter)
+{
+ DWORD retVal = gDaemonService.WinService();
+ SetEvent( gStopServiceEvent );
+ return retVal;
+}
+
+void TerminateService(void)
+{
+ gDaemonService.SetTerminateWanted();
+}
+
+DWORD Win32BackupService::WinService(void)
+{
+ 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");
+
+ const char *argv[] = {conf.c_str(), cfile.c_str()};
+
+ MAINHELPER_START
+
+ return this->Main(BOX_FILE_BBACKUPD_DEFAULT_CONFIG, argc, argv);
+
+ MAINHELPER_END
+}
+
+#endif // WIN32
diff --git a/bin/bbackupd/Win32BackupService.h b/bin/bbackupd/Win32BackupService.h
new file mode 100644
index 00000000..38cebacc
--- /dev/null
+++ b/bin/bbackupd/Win32BackupService.h
@@ -0,0 +1,21 @@
+// Box Backup service daemon implementation by Nick Knight
+
+#ifndef WIN32BACKUPSERVICE_H
+#define WIN32BACKUPSERVICE_H
+
+#ifdef WIN32
+
+class Configuration;
+class ConfigurationVerify;
+class BackupDaemon;
+
+class Win32BackupService : public BackupDaemon
+{
+public:
+ DWORD WinService(void);
+};
+
+#endif // WIN32
+
+#endif // WIN32BACKUPSERVICE_H
+
diff --git a/bin/bbackupd/Win32ServiceFunctions.cpp b/bin/bbackupd/Win32ServiceFunctions.cpp
new file mode 100644
index 00000000..89f02f62
--- /dev/null
+++ b/bin/bbackupd/Win32ServiceFunctions.cpp
@@ -0,0 +1,236 @@
+//***************************************************************
+// From the book "Win32 System Services: The Heart of Windows 98
+// and Windows 2000"
+// by Marshall Brain
+// Published by Prentice Hall
+// Copyright 1995 Prentice Hall.
+//
+// This code implements the Windows API Service interface
+// for the Box Backup for Windows native port.
+// Adapted for Box Backup by Nick Knight.
+//***************************************************************
+
+#ifdef WIN32
+
+#include "Box.h"
+
+//#include <stdio.h>
+//#include <stdlib.h>
+#include <unistd.h>
+//#include <windows.h>
+
+extern void TerminateService(void);
+extern unsigned int WINAPI RunService(LPVOID lpParameter);
+
+// Global variables
+
+TCHAR* gServiceName = TEXT("Box Backup Service");
+SERVICE_STATUS gServiceStatus;
+SERVICE_STATUS_HANDLE gServiceStatusHandle = 0;
+HANDLE gStopServiceEvent = 0;
+
+#define SERVICE_NAME "boxbackup"
+
+void ErrorHandler(char *s, DWORD err)
+{
+ char buf[256];
+ memset(buf, 0, sizeof(buf));
+ snprintf(buf, sizeof(buf)-1, "%s (%d)", s, err);
+ ::syslog(LOG_ERR, "%s", buf);
+ MessageBox(0, buf, "Error",
+ MB_OK | MB_SETFOREGROUND | MB_DEFAULT_DESKTOP_ONLY);
+ ExitProcess(err);
+}
+
+
+void WINAPI ServiceControlHandler( DWORD controlCode )
+{
+ switch ( controlCode )
+ {
+ case SERVICE_CONTROL_INTERROGATE:
+ break;
+
+ case SERVICE_CONTROL_SHUTDOWN:
+ case SERVICE_CONTROL_STOP:
+ Beep(1000,100);
+ TerminateService();
+ gServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ SetServiceStatus(gServiceStatusHandle, &gServiceStatus);
+
+ SetEvent(gStopServiceEvent);
+ return;
+
+ case SERVICE_CONTROL_PAUSE:
+ break;
+
+ case SERVICE_CONTROL_CONTINUE:
+ break;
+
+ default:
+ if ( controlCode >= 128 && controlCode <= 255 )
+ // user defined control code
+ break;
+ else
+ // unrecognised control code
+ break;
+ }
+
+ SetServiceStatus( gServiceStatusHandle, &gServiceStatus );
+}
+
+// ServiceMain is called when the SCM wants to
+// start the service. When it returns, the service
+// has stopped. It therefore waits on an event
+// just before the end of the function, and
+// that event gets set when it is time to stop.
+// It also returns on any error because the
+// service cannot start if there is an eror.
+
+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 );
+ if (!gStopServiceEvent)
+ {
+ gServiceStatus.dwControlsAccepted &=
+ ~(SERVICE_ACCEPT_STOP |
+ SERVICE_ACCEPT_SHUTDOWN);
+ gServiceStatus.dwCurrentState = SERVICE_STOPPED;
+ SetServiceStatus(gServiceStatusHandle, &gServiceStatus);
+ return;
+ }
+
+ HANDLE ourThread = (HANDLE)_beginthreadex(
+ NULL,
+ 0,
+ RunService,
+ 0,
+ CREATE_SUSPENDED,
+ NULL);
+
+ SetThreadPriority(ourThread, THREAD_PRIORITY_LOWEST);
+ ResumeThread(ourThread);
+
+ // we are now running so tell the SCM
+ gServiceStatus.dwControlsAccepted |=
+ (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
+ gServiceStatus.dwCurrentState = SERVICE_RUNNING;
+ SetServiceStatus(gServiceStatusHandle, &gServiceStatus);
+
+ // do cleanup here
+ WaitForSingleObject(gStopServiceEvent, INFINITE);
+ CloseHandle(gStopServiceEvent);
+ gStopServiceEvent = 0;
+
+ // service was stopped
+ gServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ SetServiceStatus(gServiceStatusHandle, &gServiceStatus);
+
+ // service is now stopped
+ gServiceStatus.dwControlsAccepted &=
+ ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
+ gServiceStatus.dwCurrentState = SERVICE_STOPPED;
+ SetServiceStatus(gServiceStatusHandle, &gServiceStatus);
+ }
+}
+
+void OurService(void)
+{
+ SERVICE_TABLE_ENTRY serviceTable[] =
+ {
+ { SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) ServiceMain },
+ { NULL, NULL }
+ };
+ BOOL success;
+
+ // Register with the SCM
+ success = StartServiceCtrlDispatcher(serviceTable);
+
+ if (!success)
+ {
+ ErrorHandler("Failed to start service. Did you start "
+ "Box Backup from the Service Control Manager? "
+ "(StartServiceCtrlDispatcher)", GetLastError());
+ }
+}
+
+void InstallService(void)
+{
+ SC_HANDLE newService, scm;
+
+ scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
+
+ if (!scm) return;
+
+ 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;
+
+ newService = CreateService(
+ scm,
+ SERVICE_NAME,
+ "Box Backup",
+ SERVICE_ALL_ACCESS,
+ SERVICE_WIN32_OWN_PROCESS,
+ SERVICE_DEMAND_START,
+ SERVICE_ERROR_NORMAL,
+ cmd_args,
+ 0,0,0,0,0);
+
+ if (newService) CloseServiceHandle(newService);
+ CloseServiceHandle(scm);
+}
+
+void RemoveService(void)
+{
+ SC_HANDLE service, scm;
+ SERVICE_STATUS status;
+
+ scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
+
+ if (!scm) return;
+
+ service = OpenService(scm, SERVICE_NAME, SERVICE_ALL_ACCESS|DELETE);
+ ControlService(service, SERVICE_CONTROL_STOP, &status);
+
+ if (!service)
+ {
+ printf("Failed to open service manager");
+ return;
+ }
+ if (DeleteService(service))
+ {
+ printf("Service removed");
+ }
+ else
+ {
+ printf("Failed to remove service");
+ }
+
+ CloseServiceHandle(service);
+ CloseServiceHandle(scm);
+}
+
+#endif // WIN32
diff --git a/bin/bbackupd/Win32ServiceFunctions.h b/bin/bbackupd/Win32ServiceFunctions.h
new file mode 100644
index 00000000..72f65479
--- /dev/null
+++ b/bin/bbackupd/Win32ServiceFunctions.h
@@ -0,0 +1,19 @@
+//***************************************************************
+// From the book "Win32 System Services: The Heart of Windows 98
+// and Windows 2000"
+// by Marshall Brain
+// Published by Prentice Hall
+// Copyright 1995 Prentice Hall.
+//
+// This code implements the Windows API Service interface
+// for the Box Backup for Windows native port.
+//***************************************************************
+
+#ifndef WIN32SERVICEFUNCTIONS_H
+#define WIN32SERVICEFUNCTIONS_H
+
+void RemoveService(void);
+void InstallService(void);
+void OurService(void);
+
+#endif
diff --git a/bin/bbackupd/bbackupd.cpp b/bin/bbackupd/bbackupd.cpp
index ca843105..1c870317 100755
--- a/bin/bbackupd/bbackupd.cpp
+++ b/bin/bbackupd/bbackupd.cpp
@@ -11,16 +11,84 @@
#include "BackupDaemon.h"
#include "MainHelper.h"
#include "BoxPortsAndFiles.h"
+#include "BackupStoreException.h"
#include "MemLeakFindOn.h"
+#ifdef WIN32
+ #include "Win32ServiceFunctions.h"
+ #include "Win32BackupService.h"
+
+ extern Win32BackupService gDaemonService;
+#endif
+
int main(int argc, const char *argv[])
{
MAINHELPER_START
+#ifdef WIN32
+
+ ::openlog("Box Backup (bbackupd)", 0, 0);
+
+ if(argc == 2 &&
+ (::strcmp(argv[1], "--help") == 0 ||
+ ::strcmp(argv[1], "-h") == 0))
+ {
+ printf("-h help, -i install service, -r remove service,\n"
+ "-c <config file> start daemon now");
+ return 2;
+ }
+ if(argc == 2 && ::strcmp(argv[1], "-r") == 0)
+ {
+ RemoveService();
+ return 0;
+ }
+ if(argc == 2 && ::strcmp(argv[1], "-i") == 0)
+ {
+ InstallService();
+ return 0;
+ }
+
+ // Under win32 we must initialise the Winsock library
+ // before using sockets
+
+ WSADATA info;
+
+ if (WSAStartup(MAKELONG(1, 1), &info) == SOCKET_ERROR)
+ {
+ // box backup will not run without sockets
+ ::syslog(LOG_ERR, "Failed to initialise Windows Sockets");
+ THROW_EXCEPTION(BackupStoreException, Internal)
+ }
+
+ EnableBackupRights();
+
+ int ExitCode = 0;
+
+ if (argc == 2 && ::strcmp(argv[1], "--service") == 0)
+ {
+ syslog(LOG_INFO,"Starting Box Backup Service");
+ OurService();
+ }
+ else
+ {
+ ExitCode = gDaemonService.Main(
+ BOX_FILE_BBACKUPD_DEFAULT_CONFIG, argc, argv);
+ }
+
+ // Clean up our sockets
+ WSACleanup();
+
+ ::closelog();
+
+ return ExitCode;
+
+#else // !WIN32
+
BackupDaemon daemon;
return daemon.Main(BOX_FILE_BBACKUPD_DEFAULT_CONFIG, argc, argv);
-
+
+#endif // WIN32
+
MAINHELPER_END
}
-
diff --git a/bin/bbackupd/win32/ReadMe.txt b/bin/bbackupd/win32/ReadMe.txt
new file mode 100644
index 00000000..3d260750
--- /dev/null
+++ b/bin/bbackupd/win32/ReadMe.txt
@@ -0,0 +1,24 @@
+Upgrade instructions
+
+Version 0.09g to 0.09h
+
+This version included patches to the server as well. The server for this
+upgrade can be found at http://home.earthlink.net/~gniemcew/ but hopefully
+will be merged into the core in the next release.
+
+New values in the bbackupd.conf can now be added:
+
+StoreObjectInfoFile = C:\Program Files\Box Backup\bbackupd\bbackupd.dat
+
+This stores the state when a backup daemon is shutdown.
+
+KeepAliveTime = 250
+
+This is imperative if MaximumDiffingTime is larger than 300, this stops the ssl
+layer timing out when a diff is performed. It is wise to set MaximumDiffingTime
+long enough for the largest file you may have. If you do not wish to upgrade your
+server then make KeepAliveTime greater than MaximumDiffingTime.
+
+Have fun
+
+Nick
diff --git a/bin/bbackupd/win32/bbackupd.conf b/bin/bbackupd/win32/bbackupd.conf
new file mode 100644
index 00000000..85915520
--- /dev/null
+++ b/bin/bbackupd/win32/bbackupd.conf
@@ -0,0 +1,143 @@
+
+StoreHostname = yourhost
+AccountNumber = 0x1
+KeysFile = C:\Program Files\Box Backup\1-FileEncKeys.raw
+
+CertificateFile = C:\Program Files\Box Backup\1-cert.pem
+PrivateKeyFile = C:\Program Files\Box Backup\1-key.pem
+TrustedCAsFile = C:\Program Files\Box Backup\serverCA.pem
+
+DataDirectory = C:\Program Files\Box Backup\bbackupd
+
+# If you do not install it in the default location - also do not forget to
+# change the pid file location (below)
+
+
+# This script is run whenever bbackupd encounters a problem which requires
+# the system administrator to assist:
+# 1) The store is full, and no more data can be uploaded.
+# 2) Some files or directories were not readable.
+# The default script emails the system administrator.
+
+# NotifyScript = NotifySysadmin.sh
+
+
+# A scan of the local discs will be made once an hour (approximately).
+# To avoid cycles of load on the server, this time is randomly adjusted by a small
+# percentage as the daemon runs.
+
+UpdateStoreInterval = 3600
+
+
+# A file must have been modified at least 6 hours ago before it will be uploaded.
+
+MinimumFileAge = 21600
+
+
+# If a file is modified repeated, it won't be uploaded immediately in case it's modified again.
+# However, it should be uploaded eventually. This is how long we should wait after first noticing
+# a change. (1 day)
+
+MaxUploadWait = 86400
+
+
+# Files above this size (in bytes) are tracked, and if they are renamed they will simply be
+# renamed on the server, rather than being uploaded again. (64k - 1)
+
+FileTrackingSizeThreshold = 65535
+
+
+# The daemon does "changes only" uploads for files above this size (in bytes).
+# Files less than it are uploaded whole without this extra processing.
+
+DiffingUploadSizeThreshold = 8192
+
+
+# The limit on how much time is spent diffing files. Most files shouldn't take very long,
+# but if you have really big files you can use this to limit the time spent diffing them.
+# * Reduce if you are having problems with processor usage.
+# * Increase if you have large files, and think the upload of changes is too large and want
+# to spend more time searching for unchanged blocks.
+
+MaximumDiffingTime = 20
+
+# KeepAliveTime requires Gary's SSL KeepAlive patches
+# KeepAliveTime = 250
+
+# Uncomment this line to see exactly what the daemon is going when it's connected to the server.
+
+# ExtendedLogging = yes
+
+
+# Use this to temporarily stop bbackupd from syncronising or connecting to the store.
+# This specifies a program or script script which is run just before each sync, and ideally
+# the full path to the interpreter. It will be run as the same user bbackupd is running as,
+# usually root.
+# The script prints either "now" or a number to STDOUT (and a terminating newline, no quotes).
+# If the result was "now", then the sync will happen. If it's a number, then the script will
+# be asked again in that number of seconds.
+# For example, you could use this on a laptop to only backup when on a specific network.
+
+# SyncAllowScript = /path/to/intepreter/or/exe script-name parameters etc
+
+
+# Where the command socket is created in the filesystem.
+
+CommandSocket = pipe
+
+
+Server
+{
+ PidFile = C:\Program Files\Box Backup\bbackupd\bbackupd.pid
+}
+
+# StoreObjectInfoFile requires Gary's client marker serialisation patch
+# StoreObjectInfoFile = C:\Program Files\Box Backup\bbackupd\bbackupd.dat
+
+#
+# BackupLocations specifies which locations on disc should be backed up. Each
+# directory is in the format
+#
+# name
+# {
+# Path = /path/of/directory
+# (optional exclude directives)
+# }
+#
+# 'name' is derived from the Path by the config script, but should merely be
+# unique.
+#
+# The exclude directives are of the form
+#
+# [Exclude|AlwaysInclude][File|Dir][|sRegex] = regex or full pathname
+#
+# (The regex suffix is shown as 'sRegex' to make File or Dir plural)
+#
+# For example:
+#
+# ExcludeDir = /home/guest-user
+# ExcludeFilesRegex = *.(mp3|MP3)$
+# AlwaysIncludeFile = /home/username/veryimportant.mp3
+#
+# This excludes the directory /home/guest-user from the backup along with all mp3
+# files, except one MP3 file in particular.
+#
+# In general, Exclude excludes a file or directory, unless the directory is
+# explicitly mentioned in a AlwaysInclude directive.
+#
+# If a directive ends in Regex, then it is a regular expression rather than a
+# explicit full pathname. See
+#
+# man 7 re_format
+#
+# for the regex syntax on your platform.
+#
+
+BackupLocations
+{
+ MyDocuments
+ {
+ Path = C:\Documents and Settings\
+ }
+}
+
diff --git a/bin/bbackupd/win32/installer.iss b/bin/bbackupd/win32/installer.iss
new file mode 100644
index 00000000..20e3addb
--- /dev/null
+++ b/bin/bbackupd/win32/installer.iss
@@ -0,0 +1,51 @@
+; Script to generate output file for Box Backup client for the Windows Platform
+;
+; Very important - this is the release process
+;
+; 1/ Upgrade BOX_VERSION in the file emu.h to the current version for example 0.09eWin32 - then perform a full rebuild
+;
+; 2/ Upgrade the AppVerName below to reflect the version
+;
+; 3/ Generate the output file, then rename it to the relevent filename to reflect the version
+
+[Setup]
+AppName=Box Backup
+AppVerName=BoxWin32 0.09h
+AppPublisher=Fluffy & Omniis
+AppPublisherURL=http://www.omniis.com
+AppSupportURL=http://www.omniis.com
+AppUpdatesURL=http://www.omniis.com
+DefaultDirName={pf}\Box Backup
+DefaultGroupName=Box Backup
+Compression=lzma
+SolidCompression=yes
+PrivilegesRequired=admin
+
+[Files]
+Source: "..\..\Release\bbackupd.exe"; DestDir: "{app}"; Flags: ignoreversion restartreplace
+Source: "..\..\Release\bbackupctl.exe"; DestDir: "{app}"; Flags: ignoreversion restartreplace
+Source: "..\..\Release\bbackupquery.exe"; DestDir: "{app}"; Flags: ignoreversion restartreplace
+Source: "..\..\ExceptionCodes.txt"; DestDir: "{app}"; Flags: ignoreversion restartreplace
+Source: "icon.ico"; DestDir: "{app}\"; Flags: ignoreversion restartreplace
+Source: "msvcr71.dll"; DestDir: "{app}\"; Flags: restartreplace
+Source: "bbackupd.conf"; DestDir: "{app}"; Flags: confirmoverwrite
+Source: "..\..\..\zlib\zlib1.dll"; DestDir: "{app}"; Flags: ignoreversion restartreplace
+Source: "..\..\..\openssl\bin\libeay32.dll"; DestDir: "{app}"; Flags: ignoreversion restartreplace
+Source: "..\..\..\openssl\bin\ssleay32.dll"; DestDir: "{app}"; Flags: ignoreversion restartreplace
+Source: "ReadMe.txt"; DestDir: "{app}"; Flags: ignoreversion restartreplace
+
+; NOTE: Don't use "Flags: ignoreversion" on any shared system files
+
+[Icons]
+Name: "{group}\Box Backup Query"; Filename: "{app}\bbackupquery.exe"; IconFilename: "{app}\icon.ico" ;Parameters: "-c bbackupd.conf"; WorkingDir: "{app}"
+Name: "{group}\Service\Install Service"; Filename: "{app}\bbackupd.exe"; IconFilename: "{app}\icon.ico" ;Parameters: "-i"; WorkingDir: "{app}"
+Name: "{group}\Service\Remove Service"; Filename: "{app}\bbackupd.exe"; IconFilename: "{app}\icon.ico" ;Parameters: "-r"; WorkingDir: "{app}"
+Name: "{group}\Initiate Backup Now"; Filename: "{app}\bbackupctl.exe"; IconFilename: "{app}\icon.ico" ;Parameters: "-c bbackupd.conf sync"; WorkingDir: "{app}"
+
+[Dirs]
+Name: "{app}\bbackupd"
+
+[Run]
+Filename: "{app}\bbackupd.exe"; Description: "Install Boxbackup as service"; Parameters: "-i"; Flags: postinstall
+Filename: "{app}\Readme.txt"; Description: "View upgrade notes"; Flags: postinstall shellexec skipifsilent
+