1 // distribution boxbackup-0.11_trunk_2979 (svn version: 2979)
2 // Box Backup, http://www.boxbackup.org/
3 //
4 // Copyright (c) 2003-2010, Ben Summers and contributors.
5 // All rights reserved.
6 //
7 // Note that this project uses mixed licensing. Any file with this license
8 // attached, or where the code LICENSE-GPL appears on the first line, falls
9 // under the "Box Backup GPL" license. See the file COPYING.txt for more
10 // information about this license.
11 //
12 // ---------------------------------------------------------------------
13 // This program is free software; you can redistribute it and/or
14 // modify it under the terms of the GNU General Public License
15 // as published by the Free Software Foundation; either version 2
16 // of the License, or (at your option) any later version.
17 //
18 // This program is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 // GNU General Public License for more details.
22 //
23 // You should have received a copy of the GNU General Public License
24 // along with this program; if not, write to the Free Software
25 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 //
27 // [http://www.gnu.org/licenses/old-licenses/gpl-2.0.html#SEC4]
28 //
29 // As a special exception to the GPLv2, the Box Backup Project gives
30 // permission to link any code falling under this license (the Box Backup
31 // GPL) with any software that can be downloaded from
32 // the OpenSSL website [http://www.openssl.org] under either the
33 // "OpenSSL License" or the "Original SSLeay License", and to distribute
34 // the linked executables under the terms of the "Box Backup GPL" license.
35 //
36 // As a special exception to the GPLv2, the Box Backup Project gives
37 // permission to link any code falling under this license (the Box Backup
38 // GPL) with any version of Microsoft's Volume Shadow Copy Service 7.2 SDK
39 // or Microsoft Windows Software Development Kit (SDK), including
40 // vssapi.lib, that can be downloaded from the Microsoft website
41 // [*.microsoft.com], and to distribute the linked executables under the
42 // terms of the "Box Backup GPL" license.
43 // --------------------------------------------------------------------------
44 //
45 // File
46 // Name: BackupDaemon.cpp
47 // Purpose: Backup daemon
48 // Created: 2003/10/08
49 //
50 // --------------------------------------------------------------------------
51
52 #include "Box.h"
53
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57
58 #ifdef HAVE_UNISTD_H
59 #include <unistd.h>
60 #endif
61 #ifdef HAVE_SIGNAL_H
62 #include <signal.h>
63 #endif
64 #ifdef HAVE_SYS_PARAM_H
65 #include <sys/param.h>
66 #endif
67 #ifdef HAVE_SYS_WAIT_H
68 #include <sys/wait.h>
69 #endif
70 #ifdef HAVE_SYS_MOUNT_H
71 #include <sys/mount.h>
72 #endif
73 #ifdef HAVE_MNTENT_H
74 #include <mntent.h>
75 #endif
76 #ifdef HAVE_SYS_MNTTAB_H
77 #include <cstdio>
78 #include <sys/mnttab.h>
79 #endif
80 #ifdef HAVE_PROCESS_H
81 #include <process.h>
82 #endif
83
84 #include <iostream>
85 #include <set>
86
87 #include "Configuration.h"
88 #include "IOStream.h"
89 #include "MemBlockStream.h"
90 #include "CommonException.h"
91 #include "BoxPortsAndFiles.h"
92
93 #include "SSLLib.h"
94
95 #include "autogen_BackupProtocolClient.h"
96 #include "autogen_ClientException.h"
97 #include "autogen_ConversionException.h"
98 #include "Archive.h"
99 #include "BackupClientContext.h"
100 #include "BackupClientCryptoKeys.h"
101 #include "BackupClientDirectoryRecord.h"
102 #include "BackupClientFileAttributes.h"
103 #include "BackupClientInodeToIDMap.h"
104 #include "BackupClientMakeExcludeList.h"
105 #include "BackupDaemon.h"
106 #include "BackupDaemonConfigVerify.h"
107 #include "BackupStoreConstants.h"
108 #include "BackupStoreDirectory.h"
109 #include "BackupStoreException.h"
110 #include "BackupStoreFile.h"
111 #include "BackupStoreFilenameClear.h"
112 #include "BannerText.h"
113 #include "Conversion.h"
114 #include "ExcludeList.h"
115 #include "FileStream.h"
116 #include "IOStreamGetLine.h"
117 #include "LocalProcessStream.h"
118 #include "Logging.h"
119 #include "Random.h"
120 #include "Timer.h"
121 #include "Utils.h"
122
123 #ifdef WIN32
124 #include "Win32ServiceFunctions.h"
125 #include "Win32BackupService.h"
126
127 extern Win32BackupService* gpDaemonService;
128
129 # ifdef ENABLE_VSS
130 # include <comdef.h>
131 # include <Vss.h>
132 # include <VsWriter.h>
133 # include <VsBackup.h>
134
135 // http://www.flounder.com/cstring.htm
GetMsgForHresult(HRESULT hr)136 std::string GetMsgForHresult(HRESULT hr)
137 {
138 std::ostringstream buf;
139
140 if(hr == VSS_S_ASYNC_CANCELLED)
141 {
142 buf << "VSS async operation cancelled";
143 }
144 else if(hr == VSS_S_ASYNC_FINISHED)
145 {
146 buf << "VSS async operation finished";
147 }
148 else if(hr == VSS_S_ASYNC_PENDING)
149 {
150 buf << "VSS async operation pending";
151 }
152 else
153 {
154 buf << _com_error(hr).ErrorMessage();
155 }
156
157 buf << " (" << BOX_FORMAT_HEX32(hr) << ")";
158 return buf.str();
159 }
160
WideStringToString(WCHAR * buf)161 std::string WideStringToString(WCHAR *buf)
162 {
163 char* pStr = ConvertFromWideString(buf, CP_UTF8);
164
165 if(pStr == NULL)
166 {
167 return "conversion failed";
168 }
169
170 std::string result(pStr);
171 free(pStr);
172 return result;
173 }
174
GuidToString(GUID guid)175 std::string GuidToString(GUID guid)
176 {
177 wchar_t buf[64];
178 StringFromGUID2(guid, buf, sizeof(buf));
179 return WideStringToString(buf);
180 }
181
BstrToString(const BSTR arg)182 std::string BstrToString(const BSTR arg)
183 {
184 if(arg == NULL)
185 {
186 return std::string("(null)");
187 }
188 else
189 {
190 // Extract the *long* before where the arg points to
191 long len = ((long *)arg)[-1] / 2;
192 std::wstring wstr((WCHAR *)arg, len);
193 std::string str;
194 if(!ConvertFromWideString(wstr, &str, CP_UTF8))
195 {
196 throw std::exception("string conversion failed");
197 }
198 return str;
199 }
200 }
201 # endif
202 #endif
203
204 #include "MemLeakFindOn.h"
205
206 static const time_t MAX_SLEEP_TIME = 1024;
207
208 // Make the actual sync period have a little bit of extra time, up to a 64th of the main sync period.
209 // This prevents repetative cycles of load on the server
210 #define SYNC_PERIOD_RANDOM_EXTRA_TIME_SHIFT_BY 6
211
212 // --------------------------------------------------------------------------
213 //
214 // Function
215 // Name: BackupDaemon::BackupDaemon()
216 // Purpose: constructor
217 // Created: 2003/10/08
218 //
219 // --------------------------------------------------------------------------
BackupDaemon()220 BackupDaemon::BackupDaemon()
221 : mState(BackupDaemon::State_Initialising),
222 mDeleteRedundantLocationsAfter(0),
223 mLastNotifiedEvent(SysadminNotifier::MAX),
224 mDeleteUnusedRootDirEntriesAfter(0),
225 mClientStoreMarker(BackupClientContext::ClientStoreMarker_NotKnown),
226 mStorageLimitExceeded(false),
227 mReadErrorsOnFilesystemObjects(false),
228 mLastSyncTime(0),
229 mNextSyncTime(0),
230 mCurrentSyncStartTime(0),
231 mUpdateStoreInterval(0),
232 mDeleteStoreObjectInfoFile(false),
233 mDoSyncForcedByPreviousSyncError(false),
234 mLogAllFileAccess(false),
235 mpProgressNotifier(this),
236 mpLocationResolver(this),
237 mpRunStatusProvider(this),
238 mpSysadminNotifier(this)
239 #ifdef WIN32
240 , mInstallService(false),
241 mRemoveService(false),
242 mRunAsService(false),
243 mServiceName("bbackupd")
244 #endif
245 #ifdef ENABLE_VSS
246 , mpVssBackupComponents(NULL)
247 #endif
248 {
249 // Only ever one instance of a daemon
250 SSLLib::Initialise();
251 }
252
253 // --------------------------------------------------------------------------
254 //
255 // Function
256 // Name: BackupDaemon::~BackupDaemon()
257 // Purpose: Destructor
258 // Created: 2003/10/08
259 //
260 // --------------------------------------------------------------------------
~BackupDaemon()261 BackupDaemon::~BackupDaemon()
262 {
263 DeleteAllLocations();
264 DeleteAllIDMaps();
265 }
266
267 // --------------------------------------------------------------------------
268 //
269 // Function
270 // Name: BackupDaemon::DaemonName()
271 // Purpose: Get name of daemon
272 // Created: 2003/10/08
273 //
274 // --------------------------------------------------------------------------
DaemonName() const275 const char *BackupDaemon::DaemonName() const
276 {
277 return "bbackupd";
278 }
279
280
281 // --------------------------------------------------------------------------
282 //
283 // Function
284 // Name: BackupDaemon::DaemonBanner()
285 // Purpose: Daemon banner
286 // Created: 1/1/04
287 //
288 // --------------------------------------------------------------------------
DaemonBanner() const289 std::string BackupDaemon::DaemonBanner() const
290 {
291 return BANNER_TEXT("Backup Client");
292 }
293
Usage()294 void BackupDaemon::Usage()
295 {
296 this->Daemon::Usage();
297
298 #ifdef WIN32
299 std::cout <<
300 " -s Run as a Windows Service, for internal use only\n"
301 " -i Install Windows Service (you may want to specify a config file)\n"
302 " -r Remove Windows Service\n"
303 " -S <name> Service name for -i and -r options\n";
304 #endif
305 }
306
307
308 // --------------------------------------------------------------------------
309 //
310 // Function
311 // Name: BackupDaemon::GetConfigVerify()
312 // Purpose: Get configuration specification
313 // Created: 2003/10/08
314 //
315 // --------------------------------------------------------------------------
GetConfigVerify() const316 const ConfigurationVerify *BackupDaemon::GetConfigVerify() const
317 {
318 // Defined elsewhere
319 return &BackupDaemonConfigVerify;
320 }
321
322 #ifdef PLATFORM_CANNOT_FIND_PEER_UID_OF_UNIX_SOCKET
323 // --------------------------------------------------------------------------
324 //
325 // Function
326 // Name: BackupDaemon::SetupInInitialProcess()
327 // Purpose: Platforms with non-checkable credentials on
328 // local sockets only.
329 // Prints a warning if the command socket is used.
330 // Created: 25/2/04
331 //
332 // --------------------------------------------------------------------------
SetupInInitialProcess()333 void BackupDaemon::SetupInInitialProcess()
334 {
335 // Print a warning on this platform if the CommandSocket is used.
336 if(GetConfiguration().KeyExists("CommandSocket"))
337 {
338 BOX_WARNING(
339 "==============================================================================\n"
340 "SECURITY WARNING: This platform cannot check the credentials of connections to\n"
341 "the command socket. This is a potential DoS security problem.\n"
342 "Remove the CommandSocket directive from the bbackupd.conf file if bbackupctl\n"
343 "is not used.\n"
344 "==============================================================================\n"
345 );
346 }
347 }
348 #endif
349
350
351 // --------------------------------------------------------------------------
352 //
353 // Function
354 // Name: BackupDaemon::DeleteAllLocations()
355 // Purpose: Deletes all records stored
356 // Created: 2003/10/08
357 //
358 // --------------------------------------------------------------------------
DeleteAllLocations()359 void BackupDaemon::DeleteAllLocations()
360 {
361 // Run through, and delete everything
362 for(std::vector<Location *>::iterator i = mLocations.begin();
363 i != mLocations.end(); ++i)
364 {
365 delete *i;
366 }
367
368 // Clear the contents of the map, so it is empty
369 mLocations.clear();
370
371 // And delete everything from the associated mount vector
372 mIDMapMounts.clear();
373 }
374
375 #ifdef WIN32
GetOptionString()376 std::string BackupDaemon::GetOptionString()
377 {
378 std::string oldOpts = this->Daemon::GetOptionString();
379 ASSERT(oldOpts.find("s") == std::string::npos);
380 ASSERT(oldOpts.find("S") == std::string::npos);
381 ASSERT(oldOpts.find("i") == std::string::npos);
382 ASSERT(oldOpts.find("r") == std::string::npos);
383 return oldOpts + "sS:ir";
384 }
385
ProcessOption(signed int option)386 int BackupDaemon::ProcessOption(signed int option)
387 {
388 switch(option)
389 {
390 case 's':
391 {
392 mRunAsService = true;
393 return 0;
394 }
395
396 case 'S':
397 {
398 mServiceName = optarg;
399 Logging::SetProgramName(mServiceName);
400 return 0;
401 }
402
403 case 'i':
404 {
405 mInstallService = true;
406 return 0;
407 }
408
409 case 'r':
410 {
411 mRemoveService = true;
412 return 0;
413 }
414
415 default:
416 {
417 return this->Daemon::ProcessOption(option);
418 }
419 }
420 }
421
Main(const std::string & rConfigFileName)422 int BackupDaemon::Main(const std::string &rConfigFileName)
423 {
424 if (mInstallService)
425 {
426 return InstallService(rConfigFileName.c_str(), mServiceName);
427 }
428
429 if (mRemoveService)
430 {
431 return RemoveService(mServiceName);
432 }
433
434 #ifdef ENABLE_VSS
435 HRESULT result = CoInitialize(NULL);
436 if(result != S_OK)
437 {
438 BOX_ERROR("VSS: Failed to initialize COM: " <<
439 GetMsgForHresult(result));
440 return 1;
441 }
442 #endif
443
444 int returnCode;
445
446 if (mRunAsService)
447 {
448 // We will be called reentrantly by the Service Control
449 // Manager, and we had better not call OurService again!
450 mRunAsService = false;
451
452 BOX_INFO("Box Backup service starting");
453 returnCode = OurService(rConfigFileName.c_str());
454 BOX_INFO("Box Backup service shut down");
455 }
456 else
457 {
458 returnCode = this->Daemon::Main(rConfigFileName);
459 }
460
461 return returnCode;
462 }
463 #endif
464
465 // --------------------------------------------------------------------------
466 //
467 // Function
468 // Name: BackupDaemon::Run()
469 // Purpose: Run function for daemon
470 // Created: 18/2/04
471 //
472 // --------------------------------------------------------------------------
Run()473 void BackupDaemon::Run()
474 {
475 // initialise global timer mechanism
476 Timers::Init();
477
478 #ifndef WIN32
479 // Ignore SIGPIPE so that if a command connection is broken,
480 // the daemon doesn't terminate.
481 ::signal(SIGPIPE, SIG_IGN);
482 #endif
483
484 // Create a command socket?
485 const Configuration &conf(GetConfiguration());
486 if(conf.KeyExists("CommandSocket"))
487 {
488 // Yes, create a local UNIX socket
489 mapCommandSocketInfo.reset(new CommandSocketInfo);
490 const char *socketName =
491 conf.GetKeyValue("CommandSocket").c_str();
492 #ifdef WIN32
493 mapCommandSocketInfo->mListeningSocket.Listen(
494 socketName);
495 #else
496 ::unlink(socketName);
497 mapCommandSocketInfo->mListeningSocket.Listen(
498 Socket::TypeUNIX, socketName);
499 #endif
500 }
501
502 // Handle things nicely on exceptions
503 try
504 {
505 Run2();
506 }
507 catch(...)
508 {
509 if(mapCommandSocketInfo.get())
510 {
511 try
512 {
513 mapCommandSocketInfo.reset();
514 }
515 catch(std::exception &e)
516 {
517 BOX_WARNING("Internal error while "
518 "closing command socket after "
519 "another exception: " << e.what());
520 }
521 catch(...)
522 {
523 BOX_WARNING("Error closing command socket "
524 "after exception, ignored.");
525 }
526 }
527
528 Timers::Cleanup();
529
530 throw;
531 }
532
533 // Clean up
534 mapCommandSocketInfo.reset();
535 Timers::Cleanup();
536 }
537
InitCrypto()538 void BackupDaemon::InitCrypto()
539 {
540 // Read in the certificates creating a TLS context
541 const Configuration &conf(GetConfiguration());
542 std::string certFile(conf.GetKeyValue("CertificateFile"));
543 std::string keyFile(conf.GetKeyValue("PrivateKeyFile"));
544 std::string caFile(conf.GetKeyValue("TrustedCAsFile"));
545 mTlsContext.Initialise(false /* as client */, certFile.c_str(),
546 keyFile.c_str(), caFile.c_str());
547
548 // Set up the keys for various things
549 BackupClientCryptoKeys_Setup(conf.GetKeyValue("KeysFile").c_str());
550 }
551
552 // --------------------------------------------------------------------------
553 //
554 // Function
555 // Name: BackupDaemon::Run2()
556 // Purpose: Run function for daemon (second stage)
557 // Created: 2003/10/08
558 //
559 // --------------------------------------------------------------------------
Run2()560 void BackupDaemon::Run2()
561 {
562 InitCrypto();
563
564 const Configuration &conf(GetConfiguration());
565
566 // How often to connect to the store (approximate)
567 mUpdateStoreInterval = SecondsToBoxTime(
568 conf.GetKeyValueInt("UpdateStoreInterval"));
569
570 // But are we connecting automatically?
571 bool automaticBackup = conf.GetKeyValueBool("AutomaticBackup");
572
573 // When the next sync should take place -- which is ASAP
574 mNextSyncTime = 0;
575
576 // When the last sync started (only updated if the store was not full when the sync ended)
577 mLastSyncTime = 0;
578
579 // --------------------------------------------------------------------------------------------
580
581 mDeleteStoreObjectInfoFile = DeserializeStoreObjectInfo(
582 mLastSyncTime, mNextSyncTime);
583
584 // --------------------------------------------------------------------------------------------
585
586
587 // Set state
588 SetState(State_Idle);
589
590 mDoSyncForcedByPreviousSyncError = false;
591
592 // Loop around doing backups
593 do
594 {
595 // Flags used below
596 bool storageLimitExceeded = false;
597 bool doSync = false;
598 bool mDoSyncForcedByCommand = false;
599
600 // Is a delay necessary?
601 box_time_t currentTime;
602
603 do
604 {
605 // Check whether we should be stopping,
606 // and don't run a sync if so.
607 if(StopRun()) break;
608
609 currentTime = GetCurrentBoxTime();
610
611 // Pause a while, but no more than
612 // MAX_SLEEP_TIME seconds (use the conditional
613 // because times are unsigned)
614 box_time_t requiredDelay =
615 (mNextSyncTime < currentTime)
616 ? (0)
617 : (mNextSyncTime - currentTime);
618
619 // If there isn't automatic backup happening,
620 // set a long delay. And limit delays at the
621 // same time.
622 if(!automaticBackup && !mDoSyncForcedByPreviousSyncError)
623 {
624 requiredDelay = SecondsToBoxTime(MAX_SLEEP_TIME);
625 }
626 else if(requiredDelay > SecondsToBoxTime(MAX_SLEEP_TIME))
627 {
628 requiredDelay = SecondsToBoxTime(MAX_SLEEP_TIME);
629 }
630
631 // Only delay if necessary
632 if(requiredDelay > 0)
633 {
634 // Sleep somehow. There are choices
635 // on how this should be done,
636 // depending on the state of the
637 // control connection
638 if(mapCommandSocketInfo.get() != 0)
639 {
640 // A command socket exists,
641 // so sleep by waiting on it
642 WaitOnCommandSocket(requiredDelay,
643 doSync, mDoSyncForcedByCommand);
644 }
645 else
646 {
647 // No command socket or
648 // connection, just do a
649 // normal sleep
650 time_t sleepSeconds =
651 BoxTimeToSeconds(requiredDelay);
652 ::sleep((sleepSeconds <= 0)
653 ? 1 : sleepSeconds);
654 }
655 }
656
657 if ((automaticBackup || mDoSyncForcedByPreviousSyncError)
658 && currentTime >= mNextSyncTime)
659 {
660 doSync = true;
661 }
662 }
663 while(!doSync && !StopRun());
664
665 // Time of sync start, and if it's time for another sync
666 // (and we're doing automatic syncs), set the flag
667 mCurrentSyncStartTime = GetCurrentBoxTime();
668 if((automaticBackup || mDoSyncForcedByPreviousSyncError) &&
669 mCurrentSyncStartTime >= mNextSyncTime)
670 {
671 doSync = true;
672 }
673
674 // Use a script to see if sync is allowed now?
675 if(!mDoSyncForcedByCommand && doSync && !StopRun())
676 {
677 int d = UseScriptToSeeIfSyncAllowed();
678 if(d > 0)
679 {
680 // Script has asked for a delay
681 mNextSyncTime = GetCurrentBoxTime() +
682 SecondsToBoxTime(d);
683 doSync = false;
684 }
685 }
686
687 // Ready to sync? (but only if we're not supposed
688 // to be stopping)
689 if(doSync && !StopRun())
690 {
691 RunSyncNowWithExceptionHandling();
692 }
693
694 // Set state
695 SetState(storageLimitExceeded?State_StorageLimitExceeded:State_Idle);
696
697 } while(!StopRun());
698
699 // Make sure we have a clean start next time round (if restart)
700 DeleteAllLocations();
701 DeleteAllIDMaps();
702 }
703
RunSyncNowWithExceptionHandling()704 void BackupDaemon::RunSyncNowWithExceptionHandling()
705 {
706 bool errorOccurred = false;
707 int errorCode = 0, errorSubCode = 0;
708 const char* errorString = "unknown";
709
710 try
711 {
712 OnBackupStart();
713 // Do sync
714 RunSyncNow();
715 }
716 catch(BoxException &e)
717 {
718 errorOccurred = true;
719 errorString = e.what();
720 errorCode = e.GetType();
721 errorSubCode = e.GetSubType();
722 }
723 catch(std::exception &e)
724 {
725 BOX_ERROR("Internal error during backup run: " << e.what());
726 errorOccurred = true;
727 errorString = e.what();
728 }
729 catch(...)
730 {
731 // TODO: better handling of exceptions here...
732 // need to be very careful
733 errorOccurred = true;
734 }
735
736 // do not retry immediately without a good reason
737 mDoSyncForcedByPreviousSyncError = false;
738
739 if(errorOccurred)
740 {
741 // Is it a berkely db failure?
742 bool isBerkelyDbFailure = false;
743
744 if (errorCode == BackupStoreException::ExceptionType
745 && errorSubCode == BackupStoreException::BerkelyDBFailure)
746 {
747 isBerkelyDbFailure = true;
748 }
749
750 if(isBerkelyDbFailure)
751 {
752 // Delete corrupt files
753 DeleteCorruptBerkelyDbFiles();
754 }
755
756 // Clear state data
757 // Go back to beginning of time
758 mLastSyncTime = 0;
759 mClientStoreMarker = BackupClientContext::ClientStoreMarker_NotKnown; // no store marker, so download everything
760 DeleteAllLocations();
761 DeleteAllIDMaps();
762
763 // Handle restart?
764 if(StopRun())
765 {
766 BOX_NOTICE("Exception (" << errorCode
767 << "/" << errorSubCode
768 << ") due to signal");
769 OnBackupFinish();
770 return;
771 }
772
773 NotifySysadmin(SysadminNotifier::BackupError);
774
775 // If the Berkely db files get corrupted,
776 // delete them and try again immediately.
777 if(isBerkelyDbFailure)
778 {
779 BOX_ERROR("Berkely db inode map files corrupted, "
780 "deleting and restarting scan. Renamed files "
781 "and directories will not be tracked until "
782 "after this scan.");
783 ::sleep(1);
784 }
785 else
786 {
787 // Not restart/terminate, pause and retry
788 // Notify administrator
789 SetState(State_Error);
790 BOX_ERROR("Exception caught (" << errorString <<
791 " " << errorCode << "/" << errorSubCode <<
792 "), reset state and waiting to retry...");
793 ::sleep(10);
794 mNextSyncTime = mCurrentSyncStartTime +
795 SecondsToBoxTime(100) +
796 Random::RandomInt(mUpdateStoreInterval >>
797 SYNC_PERIOD_RANDOM_EXTRA_TIME_SHIFT_BY);
798 }
799 }
800 // Notify system administrator about the final state of the backup
801 else if(mReadErrorsOnFilesystemObjects)
802 {
803 NotifySysadmin(SysadminNotifier::ReadError);
804 }
805 else if(mStorageLimitExceeded)
806 {
807 NotifySysadmin(SysadminNotifier::StoreFull);
808 }
809 else
810 {
811 NotifySysadmin(SysadminNotifier::BackupOK);
812 }
813
814 // If we were retrying after an error, and this backup succeeded,
815 // then now would be a good time to stop :-)
816 mDoSyncForcedByPreviousSyncError = errorOccurred;
817
818 OnBackupFinish();
819 }
820
RunSyncNow()821 void BackupDaemon::RunSyncNow()
822 {
823 // Delete the serialised store object file,
824 // so that we don't try to reload it after a
825 // partially completed backup
826 if(mDeleteStoreObjectInfoFile &&
827 !DeleteStoreObjectInfo())
828 {
829 BOX_ERROR("Failed to delete the StoreObjectInfoFile, "
830 "backup cannot continue safely.");
831 THROW_EXCEPTION(ClientException,
832 FailedToDeleteStoreObjectInfoFile);
833 }
834
835 // In case the backup throws an exception,
836 // we should not try to delete the store info
837 // object file again.
838 mDeleteStoreObjectInfoFile = false;
839
840 const Configuration &conf(GetConfiguration());
841
842 std::auto_ptr<FileLogger> fileLogger;
843
844 if (conf.KeyExists("LogFile"))
845 {
846 Log::Level level = Log::INFO;
847 if (conf.KeyExists("LogFileLevel"))
848 {
849 level = Logging::GetNamedLevel(
850 conf.GetKeyValue("LogFileLevel"));
851 }
852 fileLogger.reset(new FileLogger(conf.GetKeyValue("LogFile"),
853 level));
854 }
855
856 std::string extendedLogFile;
857 if (conf.KeyExists("ExtendedLogFile"))
858 {
859 extendedLogFile = conf.GetKeyValue("ExtendedLogFile");
860 }
861
862 if (conf.KeyExists("LogAllFileAccess"))
863 {
864 mLogAllFileAccess = conf.GetKeyValueBool("LogAllFileAccess");
865 }
866
867 // Then create a client context object (don't
868 // just connect, as this may be unnecessary)
869 BackupClientContext clientContext
870 (
871 *mpLocationResolver,
872 mTlsContext,
873 conf.GetKeyValue("StoreHostname"),
874 conf.GetKeyValueInt("StorePort"),
875 conf.GetKeyValueUint32("AccountNumber"),
876 conf.GetKeyValueBool("ExtendedLogging"),
877 conf.KeyExists("ExtendedLogFile"),
878 extendedLogFile, *mpProgressNotifier
879 );
880
881 // The minimum age a file needs to be before it will be
882 // considered for uploading
883 box_time_t minimumFileAge = SecondsToBoxTime(
884 conf.GetKeyValueInt("MinimumFileAge"));
885
886 // The maximum time we'll wait to upload a file, regardless
887 // of how often it's modified
888 box_time_t maxUploadWait = SecondsToBoxTime(
889 conf.GetKeyValueInt("MaxUploadWait"));
890 // Adjust by subtracting the minimum file age, so is relative
891 // to sync period end in comparisons
892 if (maxUploadWait > minimumFileAge)
893 {
894 maxUploadWait -= minimumFileAge;
895 }
896 else
897 {
898 maxUploadWait = 0;
899 }
900
901 // Calculate the sync period of files to examine
902 box_time_t syncPeriodStart = mLastSyncTime;
903 box_time_t syncPeriodEnd = GetCurrentBoxTime() - minimumFileAge;
904
905 if(syncPeriodStart >= syncPeriodEnd &&
906 syncPeriodStart - syncPeriodEnd < minimumFileAge)
907 {
908 // This can happen if we receive a force-sync command less
909 // than minimumFileAge after the last sync. Deal with it by
910 // moving back syncPeriodStart, which should not do any
911 // damage.
912 syncPeriodStart = syncPeriodEnd -
913 SecondsToBoxTime(1);
914 }
915
916 if(syncPeriodStart >= syncPeriodEnd)
917 {
918 BOX_ERROR("Invalid (negative) sync period: "
919 "perhaps your clock is going "
920 "backwards (" << syncPeriodStart <<
921 " to " << syncPeriodEnd << ")");
922 THROW_EXCEPTION(ClientException,
923 ClockWentBackwards);
924 }
925
926 // Check logic
927 ASSERT(syncPeriodEnd > syncPeriodStart);
928 // Paranoid check on sync times
929 if(syncPeriodStart >= syncPeriodEnd) return;
930
931 // Adjust syncPeriodEnd to emulate snapshot
932 // behaviour properly
933 box_time_t syncPeriodEndExtended = syncPeriodEnd;
934
935 // Using zero min file age?
936 if(minimumFileAge == 0)
937 {
938 // Add a year on to the end of the end time,
939 // to make sure we sync files which are
940 // modified after the scan run started.
941 // Of course, they may be eligible to be
942 // synced again the next time round,
943 // but this should be OK, because the changes
944 // only upload should upload no data.
945 syncPeriodEndExtended += SecondsToBoxTime(
946 (time_t)(356*24*3600));
947 }
948
949 // Set up the sync parameters
950 BackupClientDirectoryRecord::SyncParams params(*mpRunStatusProvider,
951 *mpSysadminNotifier, *mpProgressNotifier, clientContext);
952 params.mSyncPeriodStart = syncPeriodStart;
953 params.mSyncPeriodEnd = syncPeriodEndExtended;
954 // use potentially extended end time
955 params.mMaxUploadWait = maxUploadWait;
956 params.mFileTrackingSizeThreshold =
957 conf.GetKeyValueInt("FileTrackingSizeThreshold");
958 params.mDiffingUploadSizeThreshold =
959 conf.GetKeyValueInt("DiffingUploadSizeThreshold");
960 params.mMaxFileTimeInFuture =
961 SecondsToBoxTime(conf.GetKeyValueInt("MaxFileTimeInFuture"));
962
963 if(conf.KeyExists("MaxUploadRate"))
964 {
965 params.mMaxUploadRate = conf.GetKeyValueInt("MaxUploadRate");
966 }
967
968 mDeleteRedundantLocationsAfter =
969 conf.GetKeyValueInt("DeleteRedundantLocationsAfter");
970 mStorageLimitExceeded = false;
971 mReadErrorsOnFilesystemObjects = false;
972
973 // Setup various timings
974 int maximumDiffingTime = 600;
975 int keepAliveTime = 60;
976
977 // max diffing time, keep-alive time
978 if(conf.KeyExists("MaximumDiffingTime"))
979 {
980 maximumDiffingTime = conf.GetKeyValueInt("MaximumDiffingTime");
981 }
982 if(conf.KeyExists("KeepAliveTime"))
983 {
984 keepAliveTime = conf.GetKeyValueInt("KeepAliveTime");
985 }
986
987 clientContext.SetMaximumDiffingTime(maximumDiffingTime);
988 clientContext.SetKeepAliveTime(keepAliveTime);
989
990 // Set store marker
991 clientContext.SetClientStoreMarker(mClientStoreMarker);
992
993 // Set up the locations, if necessary --
994 // need to do it here so we have a
995 // (potential) connection to use
996 if(mLocations.empty())
997 {
998 const Configuration &locations(
999 conf.GetSubConfiguration(
1000 "BackupLocations"));
1001
1002 // Make sure all the directory records
1003 // are set up
1004 SetupLocations(clientContext, locations);
1005 }
1006
1007 mpProgressNotifier->NotifyIDMapsSetup(clientContext);
1008
1009 // Get some ID maps going
1010 SetupIDMapsForSync();
1011
1012 // Delete any unused directories?
1013 DeleteUnusedRootDirEntries(clientContext);
1014
1015 #ifdef ENABLE_VSS
1016 CreateVssBackupComponents();
1017 #endif
1018
1019 // Go through the records, syncing them
1020 for(std::vector<Location *>::const_iterator
1021 i(mLocations.begin());
1022 i != mLocations.end(); ++i)
1023 {
1024 // Set current and new ID map pointers
1025 // in the context
1026 clientContext.SetIDMaps(mCurrentIDMaps[(*i)->mIDMapIndex],
1027 mNewIDMaps[(*i)->mIDMapIndex]);
1028
1029 // Set exclude lists (context doesn't
1030 // take ownership)
1031 clientContext.SetExcludeLists(
1032 (*i)->mpExcludeFiles,
1033 (*i)->mpExcludeDirs);
1034
1035 // Sync the directory
1036 std::string locationPath = (*i)->mPath;
1037 #ifdef ENABLE_VSS
1038 if((*i)->mIsSnapshotCreated)
1039 {
1040 locationPath = (*i)->mSnapshotPath;
1041 }
1042 #endif
1043
1044 (*i)->mpDirectoryRecord->SyncDirectory(params,
1045 BackupProtocolClientListDirectory::RootDirectory,
1046 locationPath, std::string("/") + (*i)->mName);
1047
1048 // Unset exclude lists (just in case)
1049 clientContext.SetExcludeLists(0, 0);
1050 }
1051
1052 // Perform any deletions required -- these are
1053 // delayed until the end to allow renaming to
1054 // happen neatly.
1055 clientContext.PerformDeletions();
1056
1057 // Close any open connection
1058 clientContext.CloseAnyOpenConnection();
1059
1060 #ifdef ENABLE_VSS
1061 CleanupVssBackupComponents();
1062 #endif
1063
1064 // Get the new store marker
1065 mClientStoreMarker = clientContext.GetClientStoreMarker();
1066 mStorageLimitExceeded = clientContext.StorageLimitExceeded();
1067 mReadErrorsOnFilesystemObjects =
1068 params.mReadErrorsOnFilesystemObjects;
1069
1070 if(!mStorageLimitExceeded)
1071 {
1072 // The start time of the next run is the end time of this
1073 // run. This is only done if the storage limit wasn't
1074 // exceeded (as things won't have been done properly if
1075 // it was)
1076 mLastSyncTime = syncPeriodEnd;
1077 }
1078
1079 // Commit the ID Maps
1080 CommitIDMapsAfterSync();
1081
1082 // Calculate when the next sync run should be
1083 mNextSyncTime = mCurrentSyncStartTime +
1084 mUpdateStoreInterval +
1085 Random::RandomInt(mUpdateStoreInterval >>
1086 SYNC_PERIOD_RANDOM_EXTRA_TIME_SHIFT_BY);
1087
1088 // --------------------------------------------------------------------------------------------
1089
1090 // We had a successful backup, save the store
1091 // info. If we save successfully, we must
1092 // delete the file next time we start a backup
1093
1094 mDeleteStoreObjectInfoFile =
1095 SerializeStoreObjectInfo(mLastSyncTime,
1096 mNextSyncTime);
1097
1098 // --------------------------------------------------------------------------------------------
1099 }
1100
1101 #ifdef ENABLE_VSS
WaitForAsync(IVssAsync * pAsync,const std::string & description)1102 bool BackupDaemon::WaitForAsync(IVssAsync *pAsync,
1103 const std::string& description)
1104 {
1105 BOX_INFO("VSS: waiting for " << description << " to complete");
1106 HRESULT result;
1107
1108 do
1109 {
1110 result = pAsync->Wait(1000);
1111 if(result != S_OK)
1112 {
1113 BOX_ERROR("VSS: Failed to wait for " << description <<
1114 " to complete: " << GetMsgForHresult(result));
1115 break;
1116 }
1117
1118 HRESULT result2;
1119 result = pAsync->QueryStatus(&result2, NULL);
1120 if(result != S_OK)
1121 {
1122 BOX_ERROR("VSS: Failed to query " << description <<
1123 " status: " << GetMsgForHresult(result));
1124 break;
1125 }
1126
1127 result = result2;
1128 BOX_INFO("VSS: " << description << " status: " <<
1129 GetMsgForHresult(result));
1130 }
1131 while(result == VSS_S_ASYNC_PENDING);
1132
1133 pAsync->Release();
1134
1135 return (result == VSS_S_ASYNC_FINISHED);
1136 }
1137
1138 #define CALL_MEMBER_FN(object, method) ((object).*(method))
1139
CallAndWaitForAsync(AsyncMethod method,const std::string & description)1140 bool BackupDaemon::CallAndWaitForAsync(AsyncMethod method,
1141 const std::string& description)
1142 {
1143 IVssAsync *pAsync;
1144 HRESULT result = CALL_MEMBER_FN(*mpVssBackupComponents, method)(&pAsync);
1145 if(result != S_OK)
1146 {
1147 BOX_ERROR("VSS: " << description << " failed: " <<
1148 GetMsgForHresult(result));
1149 return false;
1150 }
1151
1152 return WaitForAsync(pAsync, description);
1153 }
1154
FreeSnapshotProp(VSS_SNAPSHOT_PROP * pSnap)1155 void FreeSnapshotProp(VSS_SNAPSHOT_PROP *pSnap)
1156 {
1157 CoTaskMemFree(pSnap->m_pwszSnapshotDeviceObject);
1158 CoTaskMemFree(pSnap->m_pwszOriginalVolumeName);
1159 CoTaskMemFree(pSnap->m_pwszOriginatingMachine);
1160 CoTaskMemFree(pSnap->m_pwszServiceMachine);
1161 CoTaskMemFree(pSnap->m_pwszExposedName);
1162 CoTaskMemFree(pSnap->m_pwszExposedPath);
1163 }
1164
CreateVssBackupComponents()1165 void BackupDaemon::CreateVssBackupComponents()
1166 {
1167 std::map<char, VSS_ID> volumesIncluded;
1168
1169 HRESULT result = ::CreateVssBackupComponents(&mpVssBackupComponents);
1170 if(result != S_OK)
1171 {
1172 BOX_ERROR("VSS: Failed to create backup components: " <<
1173 GetMsgForHresult(result));
1174 return;
1175 }
1176
1177 result = mpVssBackupComponents->InitializeForBackup(NULL);
1178 if(result != S_OK)
1179 {
1180 std::string message = GetMsgForHresult(result);
1181
1182 if (result == VSS_E_UNEXPECTED)
1183 {
1184 message = "Check the Application Log for details, and ensure "
1185 "that the Volume Shadow Copy, COM+ System Application, "
1186 "and Distributed Transaction Coordinator services "
1187 "are running";
1188 }
1189
1190 BOX_ERROR("VSS: Failed to initialize for backup: " << message);
1191 return;
1192 }
1193
1194 result = mpVssBackupComponents->SetContext(VSS_CTX_BACKUP);
1195 if(result == E_NOTIMPL)
1196 {
1197 BOX_INFO("VSS: Failed to set context to VSS_CTX_BACKUP: "
1198 "not implemented, probably Windows XP, ignored.");
1199 }
1200 else if(result != S_OK)
1201 {
1202 BOX_ERROR("VSS: Failed to set context to VSS_CTX_BACKUP: " <<
1203 GetMsgForHresult(result));
1204 return;
1205 }
1206
1207 result = mpVssBackupComponents->SetBackupState(
1208 false, /* no components for now */
1209 true, /* might as well ask for a bootable backup */
1210 VSS_BT_FULL,
1211 false /* what is Partial File Support? */);
1212 if(result != S_OK)
1213 {
1214 BOX_ERROR("VSS: Failed to set backup state: " <<
1215 GetMsgForHresult(result));
1216 return;
1217 }
1218
1219 if(!CallAndWaitForAsync(&IVssBackupComponents::GatherWriterMetadata,
1220 "GatherWriterMetadata()"))
1221 {
1222 goto CreateVssBackupComponents_cleanup_WriterMetadata;
1223 }
1224
1225 UINT writerCount;
1226 result = mpVssBackupComponents->GetWriterMetadataCount(&writerCount);
1227 if(result != S_OK)
1228 {
1229 BOX_ERROR("VSS: Failed to get writer count: " <<
1230 GetMsgForHresult(result));
1231 goto CreateVssBackupComponents_cleanup_WriterMetadata;
1232 }
1233
1234 for(UINT iWriter = 0; iWriter < writerCount; iWriter++)
1235 {
1236 BOX_INFO("VSS: Getting metadata from writer " << iWriter);
1237 VSS_ID writerInstance;
1238 IVssExamineWriterMetadata* pMetadata;
1239 result = mpVssBackupComponents->GetWriterMetadata(iWriter,
1240 &writerInstance, &pMetadata);
1241 if(result != S_OK)
1242 {
1243 BOX_ERROR("Failed to get VSS metadata from writer " << iWriter <<
1244 ": " << GetMsgForHresult(result));
1245 continue;
1246 }
1247
1248 UINT includeFiles, excludeFiles, numComponents;
1249 result = pMetadata->GetFileCounts(&includeFiles, &excludeFiles,
1250 &numComponents);
1251 if(result != S_OK)
1252 {
1253 BOX_ERROR("VSS: Failed to get metadata file counts from "
1254 "writer " << iWriter << ": " <<
1255 GetMsgForHresult(result));
1256 pMetadata->Release();
1257 continue;
1258 }
1259
1260 for(UINT iComponent = 0; iComponent < numComponents; iComponent++)
1261 {
1262 IVssWMComponent* pComponent;
1263 result = pMetadata->GetComponent(iComponent, &pComponent);
1264 if(result != S_OK)
1265 {
1266 BOX_ERROR("VSS: Failed to get metadata component " <<
1267 iComponent << " from writer " << iWriter << ": " <<
1268 GetMsgForHresult(result));
1269 continue;
1270 }
1271
1272 PVSSCOMPONENTINFO pComponentInfo;
1273 result = pComponent->GetComponentInfo(&pComponentInfo);
1274 if(result != S_OK)
1275 {
1276 BOX_ERROR("VSS: Failed to get metadata component " <<
1277 iComponent << " info from writer " << iWriter << ": " <<
1278 GetMsgForHresult(result));
1279 pComponent->Release();
1280 continue;
1281 }
1282
1283 BOX_TRACE("VSS: writer " << iWriter << " component " <<
1284 iComponent << " info:");
1285 switch(pComponentInfo->type)
1286 {
1287 case VSS_CT_UNDEFINED: BOX_INFO("VSS: type: undefined"); break;
1288 case VSS_CT_DATABASE: BOX_INFO("VSS: type: database"); break;
1289 case VSS_CT_FILEGROUP: BOX_INFO("VSS: type: filegroup"); break;
1290 default:
1291 BOX_WARNING("VSS: type: unknown (" << pComponentInfo->type << ")");
1292 }
1293
1294 BOX_TRACE("VSS: logical path: " <<
1295 BstrToString(pComponentInfo->bstrLogicalPath));
1296 BOX_TRACE("VSS: component name: " <<
1297 BstrToString(pComponentInfo->bstrComponentName));
1298 BOX_TRACE("VSS: caption: " <<
1299 BstrToString(pComponentInfo->bstrCaption));
1300 BOX_TRACE("VSS: restore metadata: " <<
1301 pComponentInfo->bRestoreMetadata);
1302 BOX_TRACE("VSS: notify on complete: " <<
1303 pComponentInfo->bRestoreMetadata);
1304 BOX_TRACE("VSS: selectable: " <<
1305 pComponentInfo->bSelectable);
1306 BOX_TRACE("VSS: selectable for restore: " <<
1307 pComponentInfo->bSelectableForRestore);
1308 BOX_TRACE("VSS: component flags: " <<
1309 BOX_FORMAT_HEX32(pComponentInfo->dwComponentFlags));
1310 BOX_TRACE("VSS: file count: " <<
1311 pComponentInfo->cFileCount);
1312 BOX_TRACE("VSS: databases: " <<
1313 pComponentInfo->cDatabases);
1314 BOX_TRACE("VSS: log files: " <<
1315 pComponentInfo->cLogFiles);
1316 BOX_TRACE("VSS: dependencies: " <<
1317 pComponentInfo->cDependencies);
1318
1319 pComponent->FreeComponentInfo(pComponentInfo);
1320 pComponent->Release();
1321 }
1322
1323 pMetadata->Release();
1324 }
1325
1326 VSS_ID snapshotSetId;
1327 result = mpVssBackupComponents->StartSnapshotSet(&snapshotSetId);
1328 if(result != S_OK)
1329 {
1330 BOX_ERROR("VSS: Failed to start snapshot set: " <<
1331 GetMsgForHresult(result));
1332 goto CreateVssBackupComponents_cleanup_WriterMetadata;
1333 }
1334
1335 // Add all volumes included as backup locations to the snapshot set
1336 for(std::vector<Location *>::iterator
1337 iLocation = mLocations.begin();
1338 iLocation != mLocations.end();
1339 iLocation++)
1340 {
1341 Location& rLocation(**iLocation);
1342 std::string path = rLocation.mPath;
1343 // convert to absolute and remove Unicode prefix
1344 path = ConvertPathToAbsoluteUnicode(path.c_str()).substr(4);
1345
1346 if(path.length() >= 3 && path[1] == ':' && path[2] == '\\')
1347 {
1348 std::string volumeRoot = path.substr(0, 3);
1349
1350 std::map<char, VSS_ID>::iterator i =
1351 volumesIncluded.find(path[0]);
1352
1353 if(i == volumesIncluded.end())
1354 {
1355 std::wstring volumeRootWide;
1356 volumeRootWide.push_back((WCHAR) path[0]);
1357 volumeRootWide.push_back((WCHAR) ':');
1358 volumeRootWide.push_back((WCHAR) '\\');
1359 VSS_ID newVolumeId;
1360 result = mpVssBackupComponents->AddToSnapshotSet(
1361 (VSS_PWSZ)(volumeRootWide.c_str()), GUID_NULL,
1362 &newVolumeId);
1363 if(result == S_OK)
1364 {
1365 BOX_TRACE("VSS: Added volume " << volumeRoot <<
1366 " for backup location " << path <<
1367 " to snapshot set");
1368 volumesIncluded[path[0]] = newVolumeId;
1369 rLocation.mSnapshotVolumeId = newVolumeId;
1370 rLocation.mIsSnapshotCreated = true;
1371 rLocation.mSnapshotPath = path;
1372 }
1373 else
1374 {
1375 BOX_ERROR("VSS: Failed to add volume " <<
1376 volumeRoot << " to snapshot set: " <<
1377 GetMsgForHresult(result));
1378 goto CreateVssBackupComponents_cleanup_WriterMetadata;
1379 }
1380 }
1381 else
1382 {
1383 BOX_TRACE("VSS: Skipping already included volume " <<
1384 volumeRoot << " for backup location " << path);
1385 rLocation.mSnapshotVolumeId = i->second;
1386 rLocation.mIsSnapshotCreated = true;
1387 }
1388 }
1389 else
1390 {
1391 BOX_WARNING("VSS: Skipping backup location " << path <<
1392 " which does not start with a volume specification");
1393 }
1394 }
1395
1396 if(!CallAndWaitForAsync(&IVssBackupComponents::PrepareForBackup,
1397 "PrepareForBackup()"))
1398 {
1399 goto CreateVssBackupComponents_cleanup_WriterMetadata;
1400 }
1401
1402 if(!CallAndWaitForAsync(&IVssBackupComponents::DoSnapshotSet,
1403 "DoSnapshotSet()"))
1404 {
1405 goto CreateVssBackupComponents_cleanup_WriterMetadata;
1406 }
1407
1408 if(!CallAndWaitForAsync(&IVssBackupComponents::GatherWriterStatus,
1409 "GatherWriterStatus()"))
1410 {
1411 goto CreateVssBackupComponents_cleanup_WriterStatus;
1412 }
1413
1414 result = mpVssBackupComponents->GetWriterStatusCount(&writerCount);
1415 if(result != S_OK)
1416 {
1417 BOX_ERROR("VSS: Failed to get writer status count: " <<
1418 GetMsgForHresult(result));
1419 goto CreateVssBackupComponents_cleanup_WriterStatus;
1420 }
1421
1422 for(UINT iWriter = 0; iWriter < writerCount; iWriter++)
1423 {
1424 VSS_ID instance, writer;
1425 BSTR writerNameBstr;
1426 VSS_WRITER_STATE writerState;
1427 HRESULT writerResult;
1428
1429 result = mpVssBackupComponents->GetWriterStatus(iWriter,
1430 &instance, &writer, &writerNameBstr, &writerState,
1431 &writerResult);
1432 if(result != S_OK)
1433 {
1434 BOX_ERROR("VSS: Failed to query writer " << iWriter <<
1435 " status: " << GetMsgForHresult(result));
1436 goto CreateVssBackupComponents_cleanup_WriterStatus;
1437 }
1438
1439 std::string writerName = BstrToString(writerNameBstr);
1440 ::SysFreeString(writerNameBstr);
1441
1442 if(writerResult != S_OK)
1443 {
1444 BOX_ERROR("VSS: Writer " << iWriter << " (" <<
1445 writerName << ") failed: " <<
1446 GetMsgForHresult(writerResult));
1447 continue;
1448 }
1449
1450 std::string stateName;
1451
1452 switch(writerState)
1453 {
1454 #define WRITER_STATE(code) \
1455 case code: stateName = #code; break;
1456 WRITER_STATE(VSS_WS_UNKNOWN);
1457 WRITER_STATE(VSS_WS_STABLE);
1458 WRITER_STATE(VSS_WS_WAITING_FOR_FREEZE);
1459 WRITER_STATE(VSS_WS_WAITING_FOR_THAW);
1460 WRITER_STATE(VSS_WS_WAITING_FOR_POST_SNAPSHOT);
1461 WRITER_STATE(VSS_WS_WAITING_FOR_BACKUP_COMPLETE);
1462 WRITER_STATE(VSS_WS_FAILED_AT_IDENTIFY);
1463 WRITER_STATE(VSS_WS_FAILED_AT_PREPARE_BACKUP);
1464 WRITER_STATE(VSS_WS_FAILED_AT_PREPARE_SNAPSHOT);
1465 WRITER_STATE(VSS_WS_FAILED_AT_FREEZE);
1466 WRITER_STATE(VSS_WS_FAILED_AT_THAW);
1467 WRITER_STATE(VSS_WS_FAILED_AT_POST_SNAPSHOT);
1468 WRITER_STATE(VSS_WS_FAILED_AT_BACKUP_COMPLETE);
1469 WRITER_STATE(VSS_WS_FAILED_AT_PRE_RESTORE);
1470 WRITER_STATE(VSS_WS_FAILED_AT_POST_RESTORE);
1471 WRITER_STATE(VSS_WS_FAILED_AT_BACKUPSHUTDOWN);
1472 #undef WRITER_STATE
1473 default:
1474 std::ostringstream o;
1475 o << "unknown (" << writerState << ")";
1476 stateName = o.str();
1477 }
1478
1479 BOX_TRACE("VSS: Writer " << iWriter << " (" <<
1480 writerName << ") is in state " << stateName);
1481 }
1482
1483 // lookup new snapshot volume for each location that has a snapshot
1484 for(std::vector<Location *>::iterator
1485 iLocation = mLocations.begin();
1486 iLocation != mLocations.end();
1487 iLocation++)
1488 {
1489 Location& rLocation(**iLocation);
1490 if(rLocation.mIsSnapshotCreated)
1491 {
1492 VSS_SNAPSHOT_PROP prop;
1493 result = mpVssBackupComponents->GetSnapshotProperties(
1494 rLocation.mSnapshotVolumeId, &prop);
1495 if(result != S_OK)
1496 {
1497 BOX_ERROR("VSS: Failed to get snapshot properties "
1498 "for volume " << GuidToString(rLocation.mSnapshotVolumeId) <<
1499 " for location " << rLocation.mPath << ": " <<
1500 GetMsgForHresult(result));
1501 rLocation.mIsSnapshotCreated = false;
1502 continue;
1503 }
1504
1505 rLocation.mSnapshotPath =
1506 WideStringToString(prop.m_pwszSnapshotDeviceObject) +
1507 DIRECTORY_SEPARATOR + rLocation.mSnapshotPath;
1508 FreeSnapshotProp(&prop);
1509
1510 BOX_INFO("VSS: Location " << rLocation.mPath << " using "
1511 "snapshot path " << rLocation.mSnapshotPath);
1512 }
1513 }
1514
1515 IVssEnumObject *pEnum;
1516 result = mpVssBackupComponents->Query(GUID_NULL, VSS_OBJECT_NONE,
1517 VSS_OBJECT_SNAPSHOT, &pEnum);
1518 if(result != S_OK)
1519 {
1520 BOX_ERROR("VSS: Failed to query snapshot list: " <<
1521 GetMsgForHresult(result));
1522 goto CreateVssBackupComponents_cleanup_WriterStatus;
1523 }
1524
1525 while(result == S_OK)
1526 {
1527 VSS_OBJECT_PROP rgelt;
1528 ULONG count;
1529 result = pEnum->Next(1, &rgelt, &count);
1530
1531 if(result == S_FALSE)
1532 {
1533 // end of list, break out of the loop
1534 break;
1535 }
1536 else if(result != S_OK)
1537 {
1538 BOX_ERROR("VSS: Failed to enumerate snapshot: " <<
1539 GetMsgForHresult(result));
1540 }
1541 else if(count != 1)
1542 {
1543 BOX_ERROR("VSS: Failed to enumerate snapshot: " <<
1544 "Next() returned " << count << " objects instead of 1");
1545 }
1546 else if(rgelt.Type != VSS_OBJECT_SNAPSHOT)
1547 {
1548 BOX_ERROR("VSS: Failed to enumerate snapshot: " <<
1549 "Next() returned a type " << rgelt.Type << " object "
1550 "instead of VSS_OBJECT_SNAPSHOT");
1551 }
1552 else
1553 {
1554 VSS_SNAPSHOT_PROP *pSnap = &rgelt.Obj.Snap;
1555 BOX_TRACE("VSS: Snapshot ID: " <<
1556 GuidToString(pSnap->m_SnapshotId));
1557 BOX_TRACE("VSS: Snapshot set ID: " <<
1558 GuidToString(pSnap->m_SnapshotSetId));
1559 BOX_TRACE("VSS: Number of volumes: " <<
1560 pSnap->m_lSnapshotsCount);
1561 BOX_TRACE("VSS: Snapshot device object: " <<
1562 WideStringToString(pSnap->m_pwszSnapshotDeviceObject));
1563 BOX_TRACE("VSS: Original volume name: " <<
1564 WideStringToString(pSnap->m_pwszOriginalVolumeName));
1565 BOX_TRACE("VSS: Originating machine: " <<
1566 WideStringToString(pSnap->m_pwszOriginatingMachine));
1567 BOX_TRACE("VSS: Service machine: " <<
1568 WideStringToString(pSnap->m_pwszServiceMachine));
1569 BOX_TRACE("VSS: Exposed name: " <<
1570 WideStringToString(pSnap->m_pwszExposedName));
1571 BOX_TRACE("VSS: Exposed path: " <<
1572 WideStringToString(pSnap->m_pwszExposedPath));
1573 BOX_TRACE("VSS: Provider ID: " <<
1574 GuidToString(pSnap->m_ProviderId));
1575 BOX_TRACE("VSS: Snapshot attributes: " <<
1576 BOX_FORMAT_HEX32(pSnap->m_lSnapshotAttributes));
1577 BOX_TRACE("VSS: Snapshot creation time: " <<
1578 BOX_FORMAT_HEX32(pSnap->m_tsCreationTimestamp));
1579
1580 std::string status;
1581 switch(pSnap->m_eStatus)
1582 {
1583 case VSS_SS_UNKNOWN: status = "Unknown (error)"; break;
1584 case VSS_SS_PREPARING: status = "Preparing"; break;
1585 case VSS_SS_PROCESSING_PREPARE: status = "Preparing (processing)"; break;
1586 case VSS_SS_PREPARED: status = "Prepared"; break;
1587 case VSS_SS_PROCESSING_PRECOMMIT: status = "Precommitting"; break;
1588 case VSS_SS_PRECOMMITTED: status = "Precommitted"; break;
1589 case VSS_SS_PROCESSING_COMMIT: status = "Commiting"; break;
1590 case VSS_SS_COMMITTED: status = "Committed"; break;
1591 case VSS_SS_PROCESSING_POSTCOMMIT: status = "Postcommitting"; break;
1592 case VSS_SS_PROCESSING_PREFINALCOMMIT: status = "Pre final committing"; break;
1593 case VSS_SS_PREFINALCOMMITTED: status = "Pre final committed"; break;
1594 case VSS_SS_PROCESSING_POSTFINALCOMMIT: status = "Post final committing"; break;
1595 case VSS_SS_CREATED: status = "Created"; break;
1596 case VSS_SS_ABORTED: status = "Aborted"; break;
1597 case VSS_SS_DELETED: status = "Deleted"; break;
1598 case VSS_SS_POSTCOMMITTED: status = "Postcommitted"; break;
1599 default:
1600 std::ostringstream buf;
1601 buf << "Unknown code: " << pSnap->m_eStatus;
1602 status = buf.str();
1603 }
1604
1605 BOX_TRACE("VSS: Snapshot status: " << status);
1606 FreeSnapshotProp(pSnap);
1607 }
1608 }
1609
1610 pEnum->Release();
1611
1612 CreateVssBackupComponents_cleanup_WriterStatus:
1613 result = mpVssBackupComponents->FreeWriterStatus();
1614 if(result != S_OK)
1615 {
1616 BOX_ERROR("VSS: Failed to free writer status: " <<
1617 GetMsgForHresult(result));
1618 }
1619
1620 CreateVssBackupComponents_cleanup_WriterMetadata:
1621 result = mpVssBackupComponents->FreeWriterMetadata();
1622 if(result != S_OK)
1623 {
1624 BOX_ERROR("VSS: Failed to free writer metadata: " <<
1625 GetMsgForHresult(result));
1626 }
1627 }
1628
CleanupVssBackupComponents()1629 void BackupDaemon::CleanupVssBackupComponents()
1630 {
1631 if(mpVssBackupComponents == NULL)
1632 {
1633 return;
1634 }
1635
1636 CallAndWaitForAsync(&IVssBackupComponents::BackupComplete,
1637 "BackupComplete()");
1638
1639 mpVssBackupComponents->Release();
1640 mpVssBackupComponents = NULL;
1641 }
1642 #endif
1643
OnBackupStart()1644 void BackupDaemon::OnBackupStart()
1645 {
1646 // Touch a file to record times in filesystem
1647 TouchFileInWorkingDir("last_sync_start");
1648
1649 // Reset statistics on uploads
1650 BackupStoreFile::ResetStats();
1651
1652 // Tell anything connected to the command socket
1653 SendSyncStartOrFinish(true /* start */);
1654
1655 // Notify administrator
1656 NotifySysadmin(SysadminNotifier::BackupStart);
1657
1658 // Set state and log start
1659 SetState(State_Connected);
1660 BOX_NOTICE("Beginning scan of local files");
1661 }
1662
OnBackupFinish()1663 void BackupDaemon::OnBackupFinish()
1664 {
1665 try
1666 {
1667 // Log
1668 BOX_NOTICE("Finished scan of local files");
1669
1670 // Log the stats
1671 BOX_NOTICE("File statistics: total file size uploaded "
1672 << BackupStoreFile::msStats.mBytesInEncodedFiles
1673 << ", bytes already on server "
1674 << BackupStoreFile::msStats.mBytesAlreadyOnServer
1675 << ", encoded size "
1676 << BackupStoreFile::msStats.mTotalFileStreamSize);
1677
1678 // Reset statistics again
1679 BackupStoreFile::ResetStats();
1680
1681 // Notify administrator
1682 NotifySysadmin(SysadminNotifier::BackupFinish);
1683
1684 // Tell anything connected to the command socket
1685 SendSyncStartOrFinish(false /* finish */);
1686
1687 // Touch a file to record times in filesystem
1688 TouchFileInWorkingDir("last_sync_finish");
1689 }
1690 catch (std::exception &e)
1691 {
1692 BOX_ERROR("Failed to perform backup finish actions: " << e.what());
1693 }
1694 }
1695
1696 // --------------------------------------------------------------------------
1697 //
1698 // Function
1699 // Name: BackupDaemon::UseScriptToSeeIfSyncAllowed()
1700 // Purpose: Private. Use a script to see if the sync should be
1701 // allowed now (if configured). Returns -1 if it's
1702 // allowed, time in seconds to wait otherwise.
1703 // Created: 21/6/04
1704 //
1705 // --------------------------------------------------------------------------
UseScriptToSeeIfSyncAllowed()1706 int BackupDaemon::UseScriptToSeeIfSyncAllowed()
1707 {
1708 const Configuration &conf(GetConfiguration());
1709
1710 // Got a script to run?
1711 if(!conf.KeyExists("SyncAllowScript"))
1712 {
1713 // No. Do sync.
1714 return -1;
1715 }
1716
1717 // If there's no result, try again in five minutes
1718 int waitInSeconds = (60*5);
1719
1720 std::string script(conf.GetKeyValue("SyncAllowScript") +
1721 " \"" + GetConfigFileName() + "\"");
1722
1723 // Run it?
1724 pid_t pid = 0;
1725 try
1726 {
1727 std::auto_ptr<IOStream> pscript(LocalProcessStream(script,
1728 pid));
1729
1730 // Read in the result
1731 IOStreamGetLine getLine(*pscript);
1732 std::string line;
1733 if(getLine.GetLine(line, true, 30000)) // 30 seconds should be enough
1734 {
1735 // Got a string, interpret
1736 if(line == "now")
1737 {
1738 // Script says do it now. Obey.
1739 waitInSeconds = -1;
1740 }
1741 else
1742 {
1743 try
1744 {
1745 // How many seconds to wait?
1746 waitInSeconds = BoxConvert::Convert<int32_t, const std::string&>(line);
1747 }
1748 catch(ConversionException &e)
1749 {
1750 BOX_ERROR("Invalid output from "
1751 "SyncAllowScript: '" <<
1752 line << "' (" << script << ")");
1753 throw;
1754 }
1755
1756 BOX_NOTICE("Delaying sync by " << waitInSeconds
1757 << " seconds due to SyncAllowScript "
1758 << "(" << script << ")");
1759 }
1760 }
1761
1762 }
1763 catch(std::exception &e)
1764 {
1765 BOX_ERROR("Internal error running SyncAllowScript: "
1766 << e.what() << " (" << script << ")");
1767 }
1768 catch(...)
1769 {
1770 // Ignore any exceptions
1771 // Log that something bad happened
1772 BOX_ERROR("Unknown error running SyncAllowScript (" <<
1773 script << ")");
1774 }
1775
1776 // Wait and then cleanup child process, if any
1777 if(pid != 0)
1778 {
1779 int status = 0;
1780 ::waitpid(pid, &status, 0);
1781 }
1782
1783 return waitInSeconds;
1784 }
1785
1786
1787
1788 // --------------------------------------------------------------------------
1789 //
1790 // Function
1791 // Name: BackupDaemon::WaitOnCommandSocket(box_time_t, bool &, bool &)
1792 // Purpose: Waits on a the command socket for a time of UP TO the required time
1793 // but may be much less, and handles a command if necessary.
1794 // Created: 18/2/04
1795 //
1796 // --------------------------------------------------------------------------
WaitOnCommandSocket(box_time_t RequiredDelay,bool & DoSyncFlagOut,bool & SyncIsForcedOut)1797 void BackupDaemon::WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFlagOut, bool &SyncIsForcedOut)
1798 {
1799 ASSERT(mapCommandSocketInfo.get());
1800 if(!mapCommandSocketInfo.get())
1801 {
1802 // failure case isn't too bad
1803 ::sleep(1);
1804 return;
1805 }
1806
1807 BOX_TRACE("Wait on command socket, delay = " << RequiredDelay);
1808
1809 try
1810 {
1811 // Timeout value for connections and things
1812 int timeout = ((int)BoxTimeToMilliSeconds(RequiredDelay)) + 1;
1813 // Handle bad boundary cases
1814 if(timeout <= 0) timeout = 1;
1815 if(timeout == INFTIM) timeout = 100000;
1816
1817 // Wait for socket connection, or handle a command?
1818 if(mapCommandSocketInfo->mpConnectedSocket.get() == 0)
1819 {
1820 // No connection, listen for a new one
1821 mapCommandSocketInfo->mpConnectedSocket.reset(mapCommandSocketInfo->mListeningSocket.Accept(timeout).release());
1822
1823 if(mapCommandSocketInfo->mpConnectedSocket.get() == 0)
1824 {
1825 // If a connection didn't arrive, there was a timeout, which means we've
1826 // waited long enough and it's time to go.
1827 return;
1828 }
1829 else
1830 {
1831 #ifdef PLATFORM_CANNOT_FIND_PEER_UID_OF_UNIX_SOCKET
1832 bool uidOK = true;
1833 BOX_WARNING("On this platform, no security check can be made on the credentials of peers connecting to the command socket. (bbackupctl)");
1834 #else
1835 // Security check -- does the process connecting to this socket have
1836 // the same UID as this process?
1837 bool uidOK = false;
1838 // BLOCK
1839 {
1840 uid_t remoteEUID = 0xffff;
1841 gid_t remoteEGID = 0xffff;
1842 if(mapCommandSocketInfo->mpConnectedSocket->GetPeerCredentials(remoteEUID, remoteEGID))
1843 {
1844 // Credentials are available -- check UID
1845 if(remoteEUID == ::getuid())
1846 {
1847 // Acceptable
1848 uidOK = true;
1849 }
1850 }
1851 }
1852 #endif // PLATFORM_CANNOT_FIND_PEER_UID_OF_UNIX_SOCKET
1853
1854 // Is this an acceptable connection?
1855 if(!uidOK)
1856 {
1857 // Dump the connection
1858 BOX_ERROR("Incoming command connection from peer had different user ID than this process, or security check could not be completed.");
1859 mapCommandSocketInfo->mpConnectedSocket.reset();
1860 return;
1861 }
1862 else
1863 {
1864 // Log
1865 BOX_INFO("Connection from command socket");
1866
1867 // Send a header line summarising the configuration and current state
1868 const Configuration &conf(GetConfiguration());
1869 char summary[256];
1870 int summarySize = sprintf(summary, "bbackupd: %d %d %d %d\nstate %d\n",
1871 conf.GetKeyValueBool("AutomaticBackup"),
1872 conf.GetKeyValueInt("UpdateStoreInterval"),
1873 conf.GetKeyValueInt("MinimumFileAge"),
1874 conf.GetKeyValueInt("MaxUploadWait"),
1875 mState);
1876 mapCommandSocketInfo->mpConnectedSocket->Write(summary, summarySize);
1877
1878 // Set the timeout to something very small, so we don't wait too long on waiting
1879 // for any incoming data
1880 timeout = 10; // milliseconds
1881 }
1882 }
1883 }
1884
1885 // So there must be a connection now.
1886 ASSERT(mapCommandSocketInfo->mpConnectedSocket.get() != 0);
1887
1888 // Is there a getline object ready?
1889 if(mapCommandSocketInfo->mpGetLine == 0)
1890 {
1891 // Create a new one
1892 mapCommandSocketInfo->mpGetLine = new IOStreamGetLine(*(mapCommandSocketInfo->mpConnectedSocket.get()));
1893 }
1894
1895 // Ping the remote side, to provide errors which will mean the socket gets closed
1896 mapCommandSocketInfo->mpConnectedSocket->Write("ping\n", 5);
1897
1898 // Wait for a command or something on the socket
1899 std::string command;
1900 while(mapCommandSocketInfo->mpGetLine != 0 && !mapCommandSocketInfo->mpGetLine->IsEOF()
1901 && mapCommandSocketInfo->mpGetLine->GetLine(command, false /* no preprocessing */, timeout))
1902 {
1903 BOX_TRACE("Receiving command '" << command
1904 << "' over command socket");
1905
1906 bool sendOK = false;
1907 bool sendResponse = true;
1908
1909 // Command to process!
1910 if(command == "quit" || command == "")
1911 {
1912 // Close the socket.
1913 CloseCommandConnection();
1914 sendResponse = false;
1915 }
1916 else if(command == "sync")
1917 {
1918 // Sync now!
1919 DoSyncFlagOut = true;
1920 SyncIsForcedOut = false;
1921 sendOK = true;
1922 }
1923 else if(command == "force-sync")
1924 {
1925 // Sync now (forced -- overrides any SyncAllowScript)
1926 DoSyncFlagOut = true;
1927 SyncIsForcedOut = true;
1928 sendOK = true;
1929 }
1930 else if(command == "reload")
1931 {
1932 // Reload the configuration
1933 SetReloadConfigWanted();
1934 sendOK = true;
1935 }
1936 else if(command == "terminate")
1937 {
1938 // Terminate the daemon cleanly
1939 SetTerminateWanted();
1940 sendOK = true;
1941 }
1942
1943 // Send a response back?
1944 if(sendResponse)
1945 {
1946 mapCommandSocketInfo->mpConnectedSocket->Write(sendOK?"ok\n":"error\n", sendOK?3:6);
1947 }
1948
1949 // Set timeout to something very small, so this just checks for data which is waiting
1950 timeout = 1;
1951 }
1952
1953 // Close on EOF?
1954 if(mapCommandSocketInfo->mpGetLine != 0 && mapCommandSocketInfo->mpGetLine->IsEOF())
1955 {
1956 CloseCommandConnection();
1957 }
1958 }
1959 catch(ConnectionException &ce)
1960 {
1961 BOX_NOTICE("Failed to write to command socket: " << ce.what());
1962
1963 // If an error occurs, and there is a connection active,
1964 // just close that connection and continue. Otherwise,
1965 // let the error propagate.
1966
1967 if(mapCommandSocketInfo->mpConnectedSocket.get() == 0)
1968 {
1969 throw; // thread will die
1970 }
1971 else
1972 {
1973 // Close socket and ignore error
1974 CloseCommandConnection();
1975 }
1976 }
1977 catch(std::exception &e)
1978 {
1979 BOX_ERROR("Failed to write to command socket: " <<
1980 e.what());
1981
1982 // If an error occurs, and there is a connection active,
1983 // just close that connection and continue. Otherwise,
1984 // let the error propagate.
1985
1986 if(mapCommandSocketInfo->mpConnectedSocket.get() == 0)
1987 {
1988 throw; // thread will die
1989 }
1990 else
1991 {
1992 // Close socket and ignore error
1993 CloseCommandConnection();
1994 }
1995 }
1996 catch(...)
1997 {
1998 BOX_ERROR("Failed to write to command socket: unknown error");
1999
2000 // If an error occurs, and there is a connection active,
2001 // just close that connection and continue. Otherwise,
2002 // let the error propagate.
2003
2004 if(mapCommandSocketInfo->mpConnectedSocket.get() == 0)
2005 {
2006 throw; // thread will die
2007 }
2008 else
2009 {
2010 // Close socket and ignore error
2011 CloseCommandConnection();
2012 }
2013 }
2014 }
2015
2016
2017 // --------------------------------------------------------------------------
2018 //
2019 // Function
2020 // Name: BackupDaemon::CloseCommandConnection()
2021 // Purpose: Close the command connection, ignoring any errors
2022 // Created: 18/2/04
2023 //
2024 // --------------------------------------------------------------------------
CloseCommandConnection()2025 void BackupDaemon::CloseCommandConnection()
2026 {
2027 try
2028 {
2029 BOX_TRACE("Closing command connection");
2030
2031 if(mapCommandSocketInfo->mpGetLine)
2032 {
2033 delete mapCommandSocketInfo->mpGetLine;
2034 mapCommandSocketInfo->mpGetLine = 0;
2035 }
2036 mapCommandSocketInfo->mpConnectedSocket.reset();
2037 }
2038 catch(std::exception &e)
2039 {
2040 BOX_ERROR("Internal error while closing command "
2041 "socket: " << e.what());
2042 }
2043 catch(...)
2044 {
2045 // Ignore any errors
2046 }
2047 }
2048
2049
2050 // --------------------------------------------------------------------------
2051 //
2052 // File
2053 // Name: BackupDaemon.cpp
2054 // Purpose: Send a start or finish sync message to the command socket, if it's connected.
2055 //
2056 // Created: 18/2/04
2057 //
2058 // --------------------------------------------------------------------------
SendSyncStartOrFinish(bool SendStart)2059 void BackupDaemon::SendSyncStartOrFinish(bool SendStart)
2060 {
2061 // The bbackupctl program can't rely on a state change, because it
2062 // may never change if the server doesn't need to be contacted.
2063
2064 if(mapCommandSocketInfo.get() &&
2065 mapCommandSocketInfo->mpConnectedSocket.get() != 0)
2066 {
2067 std::string message = SendStart ? "start-sync" : "finish-sync";
2068 try
2069 {
2070 message += "\n";
2071 mapCommandSocketInfo->mpConnectedSocket->Write(
2072 message.c_str(), message.size());
2073 }
2074 catch(std::exception &e)
2075 {
2076 BOX_ERROR("Internal error while sending to "
2077 "command socket client: " << e.what());
2078 CloseCommandConnection();
2079 }
2080 catch(...)
2081 {
2082 CloseCommandConnection();
2083 }
2084 }
2085 }
2086
2087
2088
2089
2090 #if !defined(HAVE_STRUCT_STATFS_F_MNTONNAME) && !defined(HAVE_STRUCT_STATVFS_F_NMTONNAME)
2091 // string comparison ordering for when mount points are handled
2092 // by code, rather than the OS.
2093 typedef struct
2094 {
operator ()__anon11c601ea01082095 bool operator()(const std::string &s1, const std::string &s2)
2096 {
2097 if(s1.size() == s2.size())
2098 {
2099 // Equal size, sort according to natural sort order
2100 return s1 < s2;
2101 }
2102 else
2103 {
2104 // Make sure longer strings go first
2105 return s1.size() > s2.size();
2106 }
2107 }
2108 } mntLenCompare;
2109 #endif
2110
2111 // --------------------------------------------------------------------------
2112 //
2113 // Function
2114 // Name: BackupDaemon::SetupLocations(BackupClientContext &, const Configuration &)
2115 // Purpose: Makes sure that the list of directories records is correctly set up
2116 // Created: 2003/10/08
2117 //
2118 // --------------------------------------------------------------------------
SetupLocations(BackupClientContext & rClientContext,const Configuration & rLocationsConf)2119 void BackupDaemon::SetupLocations(BackupClientContext &rClientContext, const Configuration &rLocationsConf)
2120 {
2121 if(!mLocations.empty())
2122 {
2123 // Looks correctly set up
2124 return;
2125 }
2126
2127 // Make sure that if a directory is reinstated, then it doesn't get deleted
2128 mDeleteUnusedRootDirEntriesAfter = 0;
2129 mUnusedRootDirEntries.clear();
2130
2131 // Just a check to make sure it's right.
2132 DeleteAllLocations();
2133
2134 // Going to need a copy of the root directory. Get a connection,
2135 // and fetch it.
2136 BackupProtocolClient &connection(rClientContext.GetConnection());
2137
2138 // Ask server for a list of everything in the root directory,
2139 // which is a directory itself
2140 std::auto_ptr<BackupProtocolClientSuccess> dirreply(
2141 connection.QueryListDirectory(
2142 BackupProtocolClientListDirectory::RootDirectory,
2143 // only directories
2144 BackupProtocolClientListDirectory::Flags_Dir,
2145 // exclude old/deleted stuff
2146 BackupProtocolClientListDirectory::Flags_Deleted |
2147 BackupProtocolClientListDirectory::Flags_OldVersion,
2148 false /* no attributes */));
2149
2150 // Retrieve the directory from the stream following
2151 BackupStoreDirectory dir;
2152 std::auto_ptr<IOStream> dirstream(connection.ReceiveStream());
2153 dir.ReadFromStream(*dirstream, connection.GetTimeout());
2154
2155 // Map of mount names to ID map index
2156 std::map<std::string, int> mounts;
2157 int numIDMaps = 0;
2158
2159 #ifdef HAVE_MOUNTS
2160 #if !defined(HAVE_STRUCT_STATFS_F_MNTONNAME) && !defined(HAVE_STRUCT_STATVFS_F_MNTONNAME)
2161 // Linux and others can't tell you where a directory is mounted. So we
2162 // have to read the mount entries from /etc/mtab! Bizarre that the OS
2163 // itself can't tell you, but there you go.
2164 std::set<std::string, mntLenCompare> mountPoints;
2165 // BLOCK
2166 FILE *mountPointsFile = 0;
2167
2168 #ifdef HAVE_STRUCT_MNTENT_MNT_DIR
2169 // Open mounts file
2170 mountPointsFile = ::setmntent("/proc/mounts", "r");
2171 if(mountPointsFile == 0)
2172 {
2173 mountPointsFile = ::setmntent("/etc/mtab", "r");
2174 }
2175 if(mountPointsFile == 0)
2176 {
2177 THROW_EXCEPTION(CommonException, OSFileError);
2178 }
2179
2180 try
2181 {
2182 // Read all the entries, and put them in the set
2183 struct mntent *entry = 0;
2184 while((entry = ::getmntent(mountPointsFile)) != 0)
2185 {
2186 BOX_TRACE("Found mount point at " << entry->mnt_dir);
2187 mountPoints.insert(std::string(entry->mnt_dir));
2188 }
2189
2190 // Close mounts file
2191 ::endmntent(mountPointsFile);
2192 }
2193 catch(...)
2194 {
2195 ::endmntent(mountPointsFile);
2196 throw;
2197 }
2198 #else // ! HAVE_STRUCT_MNTENT_MNT_DIR
2199 // Open mounts file
2200 mountPointsFile = ::fopen("/etc/mnttab", "r");
2201 if(mountPointsFile == 0)
2202 {
2203 THROW_EXCEPTION(CommonException, OSFileError);
2204 }
2205
2206 try
2207 {
2208 // Read all the entries, and put them in the set
2209 struct mnttab entry;
2210 while(getmntent(mountPointsFile, &entry) == 0)
2211 {
2212 BOX_TRACE("Found mount point at " << entry.mnt_mountp);
2213 mountPoints.insert(std::string(entry.mnt_mountp));
2214 }
2215
2216 // Close mounts file
2217 ::fclose(mountPointsFile);
2218 }
2219 catch(...)
2220 {
2221 ::fclose(mountPointsFile);
2222 throw;
2223 }
2224 #endif // HAVE_STRUCT_MNTENT_MNT_DIR
2225 // Check sorting and that things are as we expect
2226 ASSERT(mountPoints.size() > 0);
2227 #ifndef BOX_RELEASE_BUILD
2228 {
2229 std::set<std::string, mntLenCompare>::reverse_iterator i(mountPoints.rbegin());
2230 ASSERT(*i == "/");
2231 }
2232 #endif // n BOX_RELEASE_BUILD
2233 #endif // n HAVE_STRUCT_STATFS_F_MNTONNAME || n HAVE_STRUCT_STATVFS_F_MNTONNAME
2234 #endif // HAVE_MOUNTS
2235
2236 // Then... go through each of the entries in the configuration,
2237 // making sure there's a directory created for it.
2238 std::vector<std::string> locNames =
2239 rLocationsConf.GetSubConfigurationNames();
2240
2241 for(std::vector<std::string>::iterator
2242 pLocName = locNames.begin();
2243 pLocName != locNames.end();
2244 pLocName++)
2245 {
2246 const Configuration& rConfig(
2247 rLocationsConf.GetSubConfiguration(*pLocName));
2248 BOX_TRACE("new location: " << *pLocName);
2249
2250 // Create a record for it
2251 std::auto_ptr<Location> apLoc(new Location);
2252
2253 try
2254 {
2255 // Setup names in the location record
2256 apLoc->mName = *pLocName;
2257 apLoc->mPath = rConfig.GetKeyValue("Path");
2258
2259 // Read the exclude lists from the Configuration
2260 apLoc->mpExcludeFiles = BackupClientMakeExcludeList_Files(rConfig);
2261 apLoc->mpExcludeDirs = BackupClientMakeExcludeList_Dirs(rConfig);
2262
2263 // Does this exist on the server?
2264 // Remove from dir object early, so that if we fail
2265 // to stat the local directory, we still don't
2266 // consider to remote one for deletion.
2267 BackupStoreDirectory::Iterator iter(dir);
2268 BackupStoreFilenameClear dirname(apLoc->mName); // generate the filename
2269 BackupStoreDirectory::Entry *en = iter.FindMatchingClearName(dirname);
2270 int64_t oid = 0;
2271 if(en != 0)
2272 {
2273 oid = en->GetObjectID();
2274
2275 // Delete the entry from the directory, so we get a list of
2276 // unused root directories at the end of this.
2277 dir.DeleteEntry(oid);
2278 }
2279
2280 // Do a fsstat on the pathname to find out which mount it's on
2281 {
2282
2283 #if defined HAVE_STRUCT_STATFS_F_MNTONNAME || defined HAVE_STRUCT_STATVFS_F_MNTONNAME || defined WIN32
2284
2285 // BSD style statfs -- includes mount point, which is nice.
2286 #ifdef HAVE_STRUCT_STATVFS_F_MNTONNAME
2287 struct statvfs s;
2288 if(::statvfs(apLoc->mPath.c_str(), &s) != 0)
2289 #else // HAVE_STRUCT_STATVFS_F_MNTONNAME
2290 struct statfs s;
2291 if(::statfs(apLoc->mPath.c_str(), &s) != 0)
2292 #endif // HAVE_STRUCT_STATVFS_F_MNTONNAME
2293 {
2294 BOX_LOG_SYS_WARNING("Failed to stat location "
2295 "path '" << apLoc->mPath <<
2296 "', skipping location '" <<
2297 apLoc->mName << "'");
2298 continue;
2299 }
2300
2301 // Where the filesystem is mounted
2302 std::string mountName(s.f_mntonname);
2303
2304 #else // !HAVE_STRUCT_STATFS_F_MNTONNAME && !WIN32
2305
2306 // Warn in logs if the directory isn't absolute
2307 if(apLoc->mPath[0] != '/')
2308 {
2309 BOX_WARNING("Location path '"
2310 << apLoc->mPath
2311 << "' is not absolute");
2312 }
2313 // Go through the mount points found, and find a suitable one
2314 std::string mountName("/");
2315 {
2316 std::set<std::string, mntLenCompare>::const_iterator i(mountPoints.begin());
2317 BOX_TRACE(mountPoints.size()
2318 << " potential mount points");
2319 for(; i != mountPoints.end(); ++i)
2320 {
2321 // Compare first n characters with the filename
2322 // If it matches, the file belongs in that mount point
2323 // (sorting order ensures this)
2324 BOX_TRACE("checking against mount point " << *i);
2325 if(::strncmp(i->c_str(), apLoc->mPath.c_str(), i->size()) == 0)
2326 {
2327 // Match
2328 mountName = *i;
2329 break;
2330 }
2331 }
2332 BOX_TRACE("mount point chosen for "
2333 << apLoc->mPath << " is "
2334 << mountName);
2335 }
2336
2337 #endif
2338
2339 // Got it?
2340 std::map<std::string, int>::iterator f(mounts.find(mountName));
2341 if(f != mounts.end())
2342 {
2343 // Yes -- store the index
2344 apLoc->mIDMapIndex = f->second;
2345 }
2346 else
2347 {
2348 // No -- new index
2349 apLoc->mIDMapIndex = numIDMaps;
2350 mounts[mountName] = numIDMaps;
2351
2352 // Store the mount name
2353 mIDMapMounts.push_back(mountName);
2354
2355 // Increment number of maps
2356 ++numIDMaps;
2357 }
2358 }
2359
2360 // Does this exist on the server?
2361 if(en == 0)
2362 {
2363 // Doesn't exist, so it has to be created on the server. Let's go!
2364 // First, get the directory's attributes and modification time
2365 box_time_t attrModTime = 0;
2366 BackupClientFileAttributes attr;
2367 try
2368 {
2369 attr.ReadAttributes(apLoc->mPath.c_str(),
2370 true /* directories have zero mod times */,
2371 0 /* not interested in mod time */,
2372 &attrModTime /* get the attribute modification time */);
2373 }
2374 catch (BoxException &e)
2375 {
2376 BOX_ERROR("Failed to get attributes "
2377 "for path '" << apLoc->mPath
2378 << "', skipping location '" <<
2379 apLoc->mName << "'");
2380 continue;
2381 }
2382
2383 // Execute create directory command
2384 try
2385 {
2386 MemBlockStream attrStream(attr);
2387 std::auto_ptr<BackupProtocolClientSuccess>
2388 dirCreate(connection.QueryCreateDirectory(
2389 BackupProtocolClientListDirectory::RootDirectory,
2390 attrModTime, dirname, attrStream));
2391
2392 // Object ID for later creation
2393 oid = dirCreate->GetObjectID();
2394 }
2395 catch (BoxException &e)
2396 {
2397 BOX_ERROR("Failed to create remote "
2398 "directory '/" << apLoc->mName <<
2399 "', skipping location '" <<
2400 apLoc->mName << "'");
2401 continue;
2402 }
2403
2404 }
2405
2406 // Create and store the directory object for the root of this location
2407 ASSERT(oid != 0);
2408 BackupClientDirectoryRecord *precord =
2409 new BackupClientDirectoryRecord(oid, *pLocName);
2410 apLoc->mpDirectoryRecord.reset(precord);
2411
2412 // Push it back on the vector of locations
2413 mLocations.push_back(apLoc.release());
2414 }
2415 catch (std::exception &e)
2416 {
2417 BOX_ERROR("Failed to configure location '"
2418 << apLoc->mName << "' path '"
2419 << apLoc->mPath << "': " << e.what() <<
2420 ": please check for previous errors");
2421 throw;
2422 }
2423 catch(...)
2424 {
2425 BOX_ERROR("Failed to configure location '"
2426 << apLoc->mName << "' path '"
2427 << apLoc->mPath << "': please check for "
2428 "previous errors");
2429 throw;
2430 }
2431 }
2432
2433 // Any entries in the root directory which need deleting?
2434 if(dir.GetNumberOfEntries() > 0 &&
2435 mDeleteRedundantLocationsAfter == 0)
2436 {
2437 BOX_NOTICE(dir.GetNumberOfEntries() << " redundant locations "
2438 "in root directory found, but will not delete because "
2439 "DeleteRedundantLocationsAfter = 0");
2440 }
2441 else if(dir.GetNumberOfEntries() > 0)
2442 {
2443 box_time_t now = GetCurrentBoxTime();
2444
2445 // This should reset the timer if the list of unused
2446 // locations changes, but it will not if the number of
2447 // unused locations does not change, but the locations
2448 // do change, e.g. one mysteriously appears and another
2449 // mysteriously appears. (FIXME)
2450 if (dir.GetNumberOfEntries() != mUnusedRootDirEntries.size() ||
2451 mDeleteUnusedRootDirEntriesAfter == 0)
2452 {
2453 mDeleteUnusedRootDirEntriesAfter = now +
2454 SecondsToBoxTime(mDeleteRedundantLocationsAfter);
2455 }
2456
2457 int secs = BoxTimeToSeconds(mDeleteUnusedRootDirEntriesAfter
2458 - now);
2459
2460 BOX_NOTICE(dir.GetNumberOfEntries() << " redundant locations "
2461 "in root directory found, will delete from store "
2462 "after " << secs << " seconds.");
2463
2464 // Store directories in list of things to delete
2465 mUnusedRootDirEntries.clear();
2466 BackupStoreDirectory::Iterator iter(dir);
2467 BackupStoreDirectory::Entry *en = 0;
2468 while((en = iter.Next()) != 0)
2469 {
2470 // Add name to list
2471 BackupStoreFilenameClear clear(en->GetName());
2472 const std::string &name(clear.GetClearFilename());
2473 mUnusedRootDirEntries.push_back(
2474 std::pair<int64_t,std::string>
2475 (en->GetObjectID(), name));
2476 // Log this
2477 BOX_INFO("Unused location in root: " << name);
2478 }
2479 ASSERT(mUnusedRootDirEntries.size() > 0);
2480 }
2481 }
2482
2483
2484 // --------------------------------------------------------------------------
2485 //
2486 // Function
2487 // Name: BackupDaemon::SetupIDMapsForSync()
2488 // Purpose: Sets up ID maps for the sync process -- make sure they're all there
2489 // Created: 11/11/03
2490 //
2491 // --------------------------------------------------------------------------
SetupIDMapsForSync()2492 void BackupDaemon::SetupIDMapsForSync()
2493 {
2494 // Make sure we have some blank, empty ID maps
2495 DeleteIDMapVector(mNewIDMaps);
2496 FillIDMapVector(mNewIDMaps, true /* new maps */);
2497 DeleteIDMapVector(mCurrentIDMaps);
2498 FillIDMapVector(mCurrentIDMaps, false /* new maps */);
2499 }
2500
2501
2502 // --------------------------------------------------------------------------
2503 //
2504 // Function
2505 // Name: BackupDaemon::FillIDMapVector(std::vector<BackupClientInodeToIDMap *> &)
2506 // Purpose: Fills the vector with the right number of empty ID maps
2507 // Created: 11/11/03
2508 //
2509 // --------------------------------------------------------------------------
FillIDMapVector(std::vector<BackupClientInodeToIDMap * > & rVector,bool NewMaps)2510 void BackupDaemon::FillIDMapVector(std::vector<BackupClientInodeToIDMap *> &rVector, bool NewMaps)
2511 {
2512 ASSERT(rVector.size() == 0);
2513 rVector.reserve(mIDMapMounts.size());
2514
2515 for(unsigned int l = 0; l < mIDMapMounts.size(); ++l)
2516 {
2517 // Create the object
2518 BackupClientInodeToIDMap *pmap = new BackupClientInodeToIDMap();
2519 try
2520 {
2521 // Get the base filename of this map
2522 std::string filename;
2523 MakeMapBaseName(l, filename);
2524
2525 // If it's a new one, add a suffix
2526 if(NewMaps)
2527 {
2528 filename += ".n";
2529 }
2530
2531 // If it's not a new map, it may not exist in which case an empty map should be created
2532 if(!NewMaps && !FileExists(filename.c_str()))
2533 {
2534 pmap->OpenEmpty();
2535 }
2536 else
2537 {
2538 // Open the map
2539 pmap->Open(filename.c_str(), !NewMaps /* read only */, NewMaps /* create new */);
2540 }
2541
2542 // Store on vector
2543 rVector.push_back(pmap);
2544 }
2545 catch(...)
2546 {
2547 delete pmap;
2548 throw;
2549 }
2550 }
2551 }
2552
2553
2554 // --------------------------------------------------------------------------
2555 //
2556 // Function
2557 // Name: BackupDaemon::DeleteCorruptBerkelyDbFiles()
2558 // Purpose: Delete the Berkely db files from disc after they have been corrupted.
2559 // Created: 14/9/04
2560 //
2561 // --------------------------------------------------------------------------
DeleteCorruptBerkelyDbFiles()2562 void BackupDaemon::DeleteCorruptBerkelyDbFiles()
2563 {
2564 for(unsigned int l = 0; l < mIDMapMounts.size(); ++l)
2565 {
2566 // Get the base filename of this map
2567 std::string filename;
2568 MakeMapBaseName(l, filename);
2569
2570 // Delete the file
2571 BOX_TRACE("Deleting " << filename);
2572 ::unlink(filename.c_str());
2573
2574 // Add a suffix for the new map
2575 filename += ".n";
2576
2577 // Delete that too
2578 BOX_TRACE("Deleting " << filename);
2579 ::unlink(filename.c_str());
2580 }
2581 }
2582
2583
2584 // --------------------------------------------------------------------------
2585 //
2586 // Function
2587 // Name: MakeMapBaseName(unsigned int, std::string &)
2588 // Purpose: Makes the base name for a inode map
2589 // Created: 20/11/03
2590 //
2591 // --------------------------------------------------------------------------
MakeMapBaseName(unsigned int MountNumber,std::string & rNameOut) const2592 void BackupDaemon::MakeMapBaseName(unsigned int MountNumber, std::string &rNameOut) const
2593 {
2594 // Get the directory for the maps
2595 const Configuration &config(GetConfiguration());
2596 std::string dir(config.GetKeyValue("DataDirectory"));
2597
2598 // Make a leafname
2599 std::string leaf(mIDMapMounts[MountNumber]);
2600 for(unsigned int z = 0; z < leaf.size(); ++z)
2601 {
2602 if(leaf[z] == DIRECTORY_SEPARATOR_ASCHAR)
2603 {
2604 leaf[z] = '_';
2605 }
2606 }
2607
2608 // Build the final filename
2609 rNameOut = dir + DIRECTORY_SEPARATOR "mnt" + leaf;
2610 }
2611
2612
2613
2614
2615 // --------------------------------------------------------------------------
2616 //
2617 // Function
2618 // Name: BackupDaemon::CommitIDMapsAfterSync()
2619 // Purpose: Commits the new ID maps, so the 'new' maps are now the 'current' maps.
2620 // Created: 11/11/03
2621 //
2622 // --------------------------------------------------------------------------
CommitIDMapsAfterSync()2623 void BackupDaemon::CommitIDMapsAfterSync()
2624 {
2625 // Get rid of the maps in memory (leaving them on disc of course)
2626 DeleteIDMapVector(mCurrentIDMaps);
2627 DeleteIDMapVector(mNewIDMaps);
2628
2629 // Then move the old maps into the new places
2630 for(unsigned int l = 0; l < mIDMapMounts.size(); ++l)
2631 {
2632 std::string target;
2633 MakeMapBaseName(l, target);
2634 std::string newmap(target + ".n");
2635
2636 // Try to rename
2637 #ifdef WIN32
2638 // win32 rename doesn't overwrite existing files
2639 ::remove(target.c_str());
2640 #endif
2641 if(::rename(newmap.c_str(), target.c_str()) != 0)
2642 {
2643 BOX_LOG_SYS_ERROR("Failed to rename ID map: " <<
2644 newmap << " to " << target);
2645 THROW_EXCEPTION(CommonException, OSFileError)
2646 }
2647 }
2648 }
2649
2650
2651
2652 // --------------------------------------------------------------------------
2653 //
2654 // Function
2655 // Name: BackupDaemon::DeleteIDMapVector(std::vector<BackupClientInodeToIDMap *> &)
2656 // Purpose: Deletes the contents of a vector of ID maps
2657 // Created: 11/11/03
2658 //
2659 // --------------------------------------------------------------------------
DeleteIDMapVector(std::vector<BackupClientInodeToIDMap * > & rVector)2660 void BackupDaemon::DeleteIDMapVector(std::vector<BackupClientInodeToIDMap *> &rVector)
2661 {
2662 while(!rVector.empty())
2663 {
2664 // Pop off list
2665 BackupClientInodeToIDMap *toDel = rVector.back();
2666 rVector.pop_back();
2667
2668 // Close and delete
2669 delete toDel;
2670 }
2671 ASSERT(rVector.size() == 0);
2672 }
2673
2674
2675 // --------------------------------------------------------------------------
2676 //
2677 // Function
2678 // Name: BackupDaemon::FindLocationPathName(const std::string &, std::string &) const
2679 // Purpose: Tries to find the path of the root of a backup location. Returns true (and path in rPathOut)
2680 // if it can be found, false otherwise.
2681 // Created: 12/11/03
2682 //
2683 // --------------------------------------------------------------------------
FindLocationPathName(const std::string & rLocationName,std::string & rPathOut) const2684 bool BackupDaemon::FindLocationPathName(const std::string &rLocationName, std::string &rPathOut) const
2685 {
2686 // Search for the location
2687 for(std::vector<Location *>::const_iterator i(mLocations.begin()); i != mLocations.end(); ++i)
2688 {
2689 if((*i)->mName == rLocationName)
2690 {
2691 rPathOut = (*i)->mPath;
2692 return true;
2693 }
2694 }
2695
2696 // Didn't find it
2697 return false;
2698 }
2699
2700
2701 // --------------------------------------------------------------------------
2702 //
2703 // Function
2704 // Name: BackupDaemon::SetState(int)
2705 // Purpose: Record current action of daemon, and update process title to reflect this
2706 // Created: 11/12/03
2707 //
2708 // --------------------------------------------------------------------------
SetState(int State)2709 void BackupDaemon::SetState(int State)
2710 {
2711 // Two little checks
2712 if(State == mState) return;
2713 if(State < 0) return;
2714
2715 // Update
2716 mState = State;
2717
2718 // Set process title
2719 const static char *stateText[] = {"idle", "connected", "error -- waiting for retry", "over limit on server -- not backing up"};
2720 SetProcessTitle(stateText[State]);
2721
2722 // If there's a command socket connected, then inform it -- disconnecting from the
2723 // command socket if there's an error
2724
2725 char newState[64];
2726 sprintf(newState, "state %d", State);
2727 std::string message = newState;
2728
2729 message += "\n";
2730
2731 if(!mapCommandSocketInfo.get())
2732 {
2733 return;
2734 }
2735
2736 if(mapCommandSocketInfo->mpConnectedSocket.get() == 0)
2737 {
2738 return;
2739 }
2740
2741 // Something connected to the command socket, tell it about the new state
2742 try
2743 {
2744 mapCommandSocketInfo->mpConnectedSocket->Write(message.c_str(),
2745 message.length());
2746 }
2747 catch(ConnectionException &ce)
2748 {
2749 BOX_NOTICE("Failed to write state to command socket: " <<
2750 ce.what());
2751 CloseCommandConnection();
2752 }
2753 catch(std::exception &e)
2754 {
2755 BOX_ERROR("Failed to write state to command socket: " <<
2756 e.what());
2757 CloseCommandConnection();
2758 }
2759 catch(...)
2760 {
2761 BOX_ERROR("Failed to write state to command socket: "
2762 "unknown error");
2763 CloseCommandConnection();
2764 }
2765 }
2766
2767
2768 // --------------------------------------------------------------------------
2769 //
2770 // Function
2771 // Name: BackupDaemon::TouchFileInWorkingDir(const char *)
2772 // Purpose: Make sure a zero length file of the name exists in the working directory.
2773 // Use for marking times of events in the filesystem.
2774 // Created: 21/2/04
2775 //
2776 // --------------------------------------------------------------------------
TouchFileInWorkingDir(const char * Filename)2777 void BackupDaemon::TouchFileInWorkingDir(const char *Filename)
2778 {
2779 // Filename
2780 const Configuration &config(GetConfiguration());
2781 std::string fn(config.GetKeyValue("DataDirectory") + DIRECTORY_SEPARATOR_ASCHAR);
2782 fn += Filename;
2783
2784 // Open and close it to update the timestamp
2785 try
2786 {
2787 FileStream touch(fn, O_WRONLY | O_CREAT | O_TRUNC,
2788 S_IRUSR | S_IWUSR);
2789 }
2790 catch (std::exception &e)
2791 {
2792 BOX_ERROR("Failed to write to timestamp file: " << fn << ": " <<
2793 e.what());
2794 }
2795 }
2796
2797
2798 // --------------------------------------------------------------------------
2799 //
2800 // Function
2801 // Name: BackupDaemon::NotifySysadmin(int)
2802 // Purpose: Run the script to tell the sysadmin about events
2803 // which need attention.
2804 // Created: 25/2/04
2805 //
2806 // --------------------------------------------------------------------------
NotifySysadmin(SysadminNotifier::EventCode Event)2807 void BackupDaemon::NotifySysadmin(SysadminNotifier::EventCode Event)
2808 {
2809 static const char *sEventNames[] =
2810 {
2811 "store-full",
2812 "read-error",
2813 "backup-error",
2814 "backup-start",
2815 "backup-finish",
2816 "backup-ok",
2817 0
2818 };
2819
2820 // BOX_TRACE("sizeof(sEventNames) == " << sizeof(sEventNames));
2821 // BOX_TRACE("sizeof(*sEventNames) == " << sizeof(*sEventNames));
2822 // BOX_TRACE("NotifyEvent__MAX == " << NotifyEvent__MAX);
2823 ASSERT((sizeof(sEventNames)/sizeof(*sEventNames)) == SysadminNotifier::MAX + 1);
2824
2825 if(Event < 0 || Event >= SysadminNotifier::MAX)
2826 {
2827 BOX_ERROR("BackupDaemon::NotifySysadmin() called for "
2828 "invalid event code " << Event);
2829 THROW_EXCEPTION(BackupStoreException,
2830 BadNotifySysadminEventCode);
2831 }
2832
2833 BOX_TRACE("BackupDaemon::NotifySysadmin() called, event = " <<
2834 sEventNames[Event]);
2835
2836 if(!GetConfiguration().KeyExists("NotifyAlways") ||
2837 !GetConfiguration().GetKeyValueBool("NotifyAlways"))
2838 {
2839 // Don't send lots of repeated messages
2840 // Note: backup-start and backup-finish will always be
2841 // logged, because mLastNotifiedEvent is never set to
2842 // these values and therefore they are never "duplicates".
2843 if(mLastNotifiedEvent == Event)
2844 {
2845 if(Event == SysadminNotifier::BackupOK)
2846 {
2847 BOX_INFO("Suppressing duplicate notification "
2848 "about " << sEventNames[Event]);
2849 }
2850 else
2851 {
2852 BOX_WARNING("Suppressing duplicate notification "
2853 "about " << sEventNames[Event]);
2854 }
2855 return;
2856 }
2857 }
2858
2859 // Is there a notification script?
2860 const Configuration &conf(GetConfiguration());
2861 if(!conf.KeyExists("NotifyScript"))
2862 {
2863 // Log, and then return
2864 if(Event != SysadminNotifier::BackupStart &&
2865 Event != SysadminNotifier::BackupFinish)
2866 {
2867 BOX_INFO("Not notifying administrator about event "
2868 << sEventNames[Event] << ", set NotifyScript "
2869 "to do this in future");
2870 }
2871 return;
2872 }
2873
2874 // Script to run
2875 std::string script(conf.GetKeyValue("NotifyScript") + " " +
2876 sEventNames[Event] + " \"" + GetConfigFileName() + "\"");
2877
2878 // Log what we're about to do
2879 BOX_INFO("About to notify administrator about event "
2880 << sEventNames[Event] << ", running script '" << script << "'");
2881
2882 // Then do it
2883 int returnCode = ::system(script.c_str());
2884 if(returnCode != 0)
2885 {
2886 BOX_WARNING("Notify script returned error code: " <<
2887 returnCode << " (" << script << ")");
2888 }
2889 else if(Event != SysadminNotifier::BackupStart &&
2890 Event != SysadminNotifier::BackupFinish)
2891 {
2892 mLastNotifiedEvent = Event;
2893 }
2894 }
2895
2896
2897 // --------------------------------------------------------------------------
2898 //
2899 // Function
2900 // Name: BackupDaemon::DeleteUnusedRootDirEntries(BackupClientContext &)
2901 // Purpose: Deletes any unused entries in the root directory, if they're scheduled to be deleted.
2902 // Created: 13/5/04
2903 //
2904 // --------------------------------------------------------------------------
DeleteUnusedRootDirEntries(BackupClientContext & rContext)2905 void BackupDaemon::DeleteUnusedRootDirEntries(BackupClientContext &rContext)
2906 {
2907 if(mUnusedRootDirEntries.empty())
2908 {
2909 BOX_INFO("Not deleting unused entries - none in list");
2910 return;
2911 }
2912
2913 if(mDeleteUnusedRootDirEntriesAfter == 0)
2914 {
2915 BOX_INFO("Not deleting unused entries - "
2916 "zero delete time (bad)");
2917 return;
2918 }
2919
2920 // Check time
2921 box_time_t now = GetCurrentBoxTime();
2922 if(now < mDeleteUnusedRootDirEntriesAfter)
2923 {
2924 int secs = BoxTimeToSeconds(mDeleteUnusedRootDirEntriesAfter
2925 - now);
2926 BOX_INFO("Not deleting unused entries - too early ("
2927 << secs << " seconds remaining)");
2928 return;
2929 }
2930
2931 // Entries to delete, and it's the right time to do so...
2932 BOX_NOTICE("Deleting unused locations from store root...");
2933 BackupProtocolClient &connection(rContext.GetConnection());
2934 for(std::vector<std::pair<int64_t,std::string> >::iterator
2935 i(mUnusedRootDirEntries.begin());
2936 i != mUnusedRootDirEntries.end(); ++i)
2937 {
2938 connection.QueryDeleteDirectory(i->first);
2939 rContext.GetProgressNotifier().NotifyFileDeleted(
2940 i->first, i->second);
2941 }
2942
2943 // Reset state
2944 mDeleteUnusedRootDirEntriesAfter = 0;
2945 mUnusedRootDirEntries.clear();
2946 }
2947
2948 // --------------------------------------------------------------------------
2949
2950 typedef struct
2951 {
2952 int32_t mMagicValue; // also the version number
2953 int32_t mNumEntries;
2954 int64_t mObjectID; // this object ID
2955 int64_t mContainerID; // ID of container
2956 uint64_t mAttributesModTime;
2957 int32_t mOptionsPresent; // bit mask of optional sections / features present
2958
2959 } loc_StreamFormat;
2960
2961 // --------------------------------------------------------------------------
2962 //
2963 // Function
2964 // Name: BackupDaemon::Location::Location()
2965 // Purpose: Constructor
2966 // Created: 11/11/03
2967 //
2968 // --------------------------------------------------------------------------
Location()2969 BackupDaemon::Location::Location()
2970 : mIDMapIndex(0),
2971 mpExcludeFiles(0),
2972 mpExcludeDirs(0)
2973 {
2974 }
2975
2976 // --------------------------------------------------------------------------
2977 //
2978 // Function
2979 // Name: BackupDaemon::Location::~Location()
2980 // Purpose: Destructor
2981 // Created: 11/11/03
2982 //
2983 // --------------------------------------------------------------------------
~Location()2984 BackupDaemon::Location::~Location()
2985 {
2986 // Clean up exclude locations
2987 if(mpExcludeDirs != 0)
2988 {
2989 delete mpExcludeDirs;
2990 mpExcludeDirs = 0;
2991 }
2992 if(mpExcludeFiles != 0)
2993 {
2994 delete mpExcludeFiles;
2995 mpExcludeFiles = 0;
2996 }
2997 }
2998
2999 // --------------------------------------------------------------------------
3000 //
3001 // Function
3002 // Name: BackupDaemon::Location::Deserialize(Archive & rArchive)
3003 // Purpose: Deserializes this object instance from a stream of bytes, using an Archive abstraction.
3004 //
3005 // Created: 2005/04/11
3006 //
3007 // --------------------------------------------------------------------------
Deserialize(Archive & rArchive)3008 void BackupDaemon::Location::Deserialize(Archive &rArchive)
3009 {
3010 //
3011 //
3012 //
3013 mpDirectoryRecord.reset(NULL);
3014 if(mpExcludeFiles)
3015 {
3016 delete mpExcludeFiles;
3017 mpExcludeFiles = NULL;
3018 }
3019 if(mpExcludeDirs)
3020 {
3021 delete mpExcludeDirs;
3022 mpExcludeDirs = NULL;
3023 }
3024
3025 //
3026 //
3027 //
3028 rArchive.Read(mName);
3029 rArchive.Read(mPath);
3030 rArchive.Read(mIDMapIndex);
3031
3032 //
3033 //
3034 //
3035 int64_t aMagicMarker = 0;
3036 rArchive.Read(aMagicMarker);
3037
3038 if(aMagicMarker == ARCHIVE_MAGIC_VALUE_NOOP)
3039 {
3040 // NOOP
3041 }
3042 else if(aMagicMarker == ARCHIVE_MAGIC_VALUE_RECURSE)
3043 {
3044 BackupClientDirectoryRecord *pSubRecord = new BackupClientDirectoryRecord(0, "");
3045 if(!pSubRecord)
3046 {
3047 throw std::bad_alloc();
3048 }
3049
3050 mpDirectoryRecord.reset(pSubRecord);
3051 mpDirectoryRecord->Deserialize(rArchive);
3052 }
3053 else
3054 {
3055 // there is something going on here
3056 THROW_EXCEPTION(ClientException, CorruptStoreObjectInfoFile);
3057 }
3058
3059 //
3060 //
3061 //
3062 rArchive.Read(aMagicMarker);
3063
3064 if(aMagicMarker == ARCHIVE_MAGIC_VALUE_NOOP)
3065 {
3066 // NOOP
3067 }
3068 else if(aMagicMarker == ARCHIVE_MAGIC_VALUE_RECURSE)
3069 {
3070 mpExcludeFiles = new ExcludeList;
3071 if(!mpExcludeFiles)
3072 {
3073 throw std::bad_alloc();
3074 }
3075
3076 mpExcludeFiles->Deserialize(rArchive);
3077 }
3078 else
3079 {
3080 // there is something going on here
3081 THROW_EXCEPTION(ClientException, CorruptStoreObjectInfoFile);
3082 }
3083
3084 //
3085 //
3086 //
3087 rArchive.Read(aMagicMarker);
3088
3089 if(aMagicMarker == ARCHIVE_MAGIC_VALUE_NOOP)
3090 {
3091 // NOOP
3092 }
3093 else if(aMagicMarker == ARCHIVE_MAGIC_VALUE_RECURSE)
3094 {
3095 mpExcludeDirs = new ExcludeList;
3096 if(!mpExcludeDirs)
3097 {
3098 throw std::bad_alloc();
3099 }
3100
3101 mpExcludeDirs->Deserialize(rArchive);
3102 }
3103 else
3104 {
3105 // there is something going on here
3106 THROW_EXCEPTION(ClientException, CorruptStoreObjectInfoFile);
3107 }
3108 }
3109
3110 // --------------------------------------------------------------------------
3111 //
3112 // Function
3113 // Name: BackupDaemon::Location::Serialize(Archive & rArchive)
3114 // Purpose: Serializes this object instance into a stream of bytes, using an Archive abstraction.
3115 //
3116 // Created: 2005/04/11
3117 //
3118 // --------------------------------------------------------------------------
Serialize(Archive & rArchive) const3119 void BackupDaemon::Location::Serialize(Archive & rArchive) const
3120 {
3121 //
3122 //
3123 //
3124 rArchive.Write(mName);
3125 rArchive.Write(mPath);
3126 rArchive.Write(mIDMapIndex);
3127
3128 //
3129 //
3130 //
3131 if(mpDirectoryRecord.get() == NULL)
3132 {
3133 int64_t aMagicMarker = ARCHIVE_MAGIC_VALUE_NOOP;
3134 rArchive.Write(aMagicMarker);
3135 }
3136 else
3137 {
3138 int64_t aMagicMarker = ARCHIVE_MAGIC_VALUE_RECURSE; // be explicit about whether recursion follows
3139 rArchive.Write(aMagicMarker);
3140
3141 mpDirectoryRecord->Serialize(rArchive);
3142 }
3143
3144 //
3145 //
3146 //
3147 if(!mpExcludeFiles)
3148 {
3149 int64_t aMagicMarker = ARCHIVE_MAGIC_VALUE_NOOP;
3150 rArchive.Write(aMagicMarker);
3151 }
3152 else
3153 {
3154 int64_t aMagicMarker = ARCHIVE_MAGIC_VALUE_RECURSE; // be explicit about whether recursion follows
3155 rArchive.Write(aMagicMarker);
3156
3157 mpExcludeFiles->Serialize(rArchive);
3158 }
3159
3160 //
3161 //
3162 //
3163 if(!mpExcludeDirs)
3164 {
3165 int64_t aMagicMarker = ARCHIVE_MAGIC_VALUE_NOOP;
3166 rArchive.Write(aMagicMarker);
3167 }
3168 else
3169 {
3170 int64_t aMagicMarker = ARCHIVE_MAGIC_VALUE_RECURSE; // be explicit about whether recursion follows
3171 rArchive.Write(aMagicMarker);
3172
3173 mpExcludeDirs->Serialize(rArchive);
3174 }
3175 }
3176
3177 // --------------------------------------------------------------------------
3178 //
3179 // Function
3180 // Name: BackupDaemon::CommandSocketInfo::CommandSocketInfo()
3181 // Purpose: Constructor
3182 // Created: 18/2/04
3183 //
3184 // --------------------------------------------------------------------------
CommandSocketInfo()3185 BackupDaemon::CommandSocketInfo::CommandSocketInfo()
3186 : mpGetLine(0)
3187 {
3188 }
3189
3190
3191 // --------------------------------------------------------------------------
3192 //
3193 // Function
3194 // Name: BackupDaemon::CommandSocketInfo::~CommandSocketInfo()
3195 // Purpose: Destructor
3196 // Created: 18/2/04
3197 //
3198 // --------------------------------------------------------------------------
~CommandSocketInfo()3199 BackupDaemon::CommandSocketInfo::~CommandSocketInfo()
3200 {
3201 if(mpGetLine)
3202 {
3203 delete mpGetLine;
3204 mpGetLine = 0;
3205 }
3206 }
3207
3208 // --------------------------------------------------------------------------
3209 //
3210 // Function
3211 // Name: BackupDaemon::SerializeStoreObjectInfo(
3212 // box_time_t theLastSyncTime,
3213 // box_time_t theNextSyncTime)
3214 // Purpose: Serializes remote directory and file information
3215 // into a stream of bytes, using an Archive
3216 // abstraction.
3217 // Created: 2005/04/11
3218 //
3219 // --------------------------------------------------------------------------
3220
3221 static const int STOREOBJECTINFO_MAGIC_ID_VALUE = 0x7777525F;
3222 static const std::string STOREOBJECTINFO_MAGIC_ID_STRING = "BBACKUPD-STATE";
3223 static const int STOREOBJECTINFO_VERSION = 2;
3224
SerializeStoreObjectInfo(box_time_t theLastSyncTime,box_time_t theNextSyncTime) const3225 bool BackupDaemon::SerializeStoreObjectInfo(box_time_t theLastSyncTime,
3226 box_time_t theNextSyncTime) const
3227 {
3228 if(!GetConfiguration().KeyExists("StoreObjectInfoFile"))
3229 {
3230 return false;
3231 }
3232
3233 std::string StoreObjectInfoFile =
3234 GetConfiguration().GetKeyValue("StoreObjectInfoFile");
3235
3236 if(StoreObjectInfoFile.size() <= 0)
3237 {
3238 return false;
3239 }
3240
3241 bool created = false;
3242
3243 try
3244 {
3245 FileStream aFile(StoreObjectInfoFile.c_str(),
3246 O_WRONLY | O_CREAT | O_TRUNC);
3247 created = true;
3248
3249 Archive anArchive(aFile, 0);
3250
3251 anArchive.Write(STOREOBJECTINFO_MAGIC_ID_VALUE);
3252 anArchive.Write(STOREOBJECTINFO_MAGIC_ID_STRING);
3253 anArchive.Write(STOREOBJECTINFO_VERSION);
3254 anArchive.Write(GetLoadedConfigModifiedTime());
3255 anArchive.Write(mClientStoreMarker);
3256 anArchive.Write(theLastSyncTime);
3257 anArchive.Write(theNextSyncTime);
3258
3259 //
3260 //
3261 //
3262 int64_t iCount = mLocations.size();
3263 anArchive.Write(iCount);
3264
3265 for(int v = 0; v < iCount; v++)
3266 {
3267 ASSERT(mLocations[v]);
3268 mLocations[v]->Serialize(anArchive);
3269 }
3270
3271 //
3272 //
3273 //
3274 iCount = mIDMapMounts.size();
3275 anArchive.Write(iCount);
3276
3277 for(int v = 0; v < iCount; v++)
3278 anArchive.Write(mIDMapMounts[v]);
3279
3280 //
3281 //
3282 //
3283 iCount = mUnusedRootDirEntries.size();
3284 anArchive.Write(iCount);
3285
3286 for(int v = 0; v < iCount; v++)
3287 {
3288 anArchive.Write(mUnusedRootDirEntries[v].first);
3289 anArchive.Write(mUnusedRootDirEntries[v].second);
3290 }
3291
3292 if (iCount > 0)
3293 {
3294 anArchive.Write(mDeleteUnusedRootDirEntriesAfter);
3295 }
3296
3297 //
3298 //
3299 //
3300 aFile.Close();
3301 BOX_INFO("Saved store object info file version " <<
3302 STOREOBJECTINFO_VERSION << " (" <<
3303 StoreObjectInfoFile << ")");
3304 }
3305 catch(std::exception &e)
3306 {
3307 BOX_ERROR("Failed to write StoreObjectInfoFile: " <<
3308 StoreObjectInfoFile << ": " << e.what());
3309 }
3310 catch(...)
3311 {
3312 BOX_ERROR("Failed to write StoreObjectInfoFile: " <<
3313 StoreObjectInfoFile << ": unknown error");
3314 }
3315
3316 return created;
3317 }
3318
3319 // --------------------------------------------------------------------------
3320 //
3321 // Function
3322 // Name: BackupDaemon::DeserializeStoreObjectInfo(
3323 // box_time_t & theLastSyncTime,
3324 // box_time_t & theNextSyncTime)
3325 // Purpose: Deserializes remote directory and file information
3326 // from a stream of bytes, using an Archive
3327 // abstraction.
3328 // Created: 2005/04/11
3329 //
3330 // --------------------------------------------------------------------------
DeserializeStoreObjectInfo(box_time_t & theLastSyncTime,box_time_t & theNextSyncTime)3331 bool BackupDaemon::DeserializeStoreObjectInfo(box_time_t & theLastSyncTime,
3332 box_time_t & theNextSyncTime)
3333 {
3334 //
3335 //
3336 //
3337 DeleteAllLocations();
3338
3339 //
3340 //
3341 //
3342 if(!GetConfiguration().KeyExists("StoreObjectInfoFile"))
3343 {
3344 return false;
3345 }
3346
3347 std::string StoreObjectInfoFile =
3348 GetConfiguration().GetKeyValue("StoreObjectInfoFile");
3349
3350 if(StoreObjectInfoFile.size() <= 0)
3351 {
3352 return false;
3353 }
3354
3355 try
3356 {
3357 FileStream aFile(StoreObjectInfoFile.c_str(), O_RDONLY);
3358 Archive anArchive(aFile, 0);
3359
3360 //
3361 // see if the content looks like a valid serialised archive
3362 //
3363 int iMagicValue = 0;
3364 anArchive.Read(iMagicValue);
3365
3366 if(iMagicValue != STOREOBJECTINFO_MAGIC_ID_VALUE)
3367 {
3368 BOX_WARNING("Store object info file "
3369 "is not a valid or compatible serialised "
3370 "archive. Will re-cache from store. "
3371 "(" << StoreObjectInfoFile << ")");
3372 return false;
3373 }
3374
3375 //
3376 // get a bit optimistic and read in a string identifier
3377 //
3378 std::string strMagicValue;
3379 anArchive.Read(strMagicValue);
3380
3381 if(strMagicValue != STOREOBJECTINFO_MAGIC_ID_STRING)
3382 {
3383 BOX_WARNING("Store object info file "
3384 "is not a valid or compatible serialised "
3385 "archive. Will re-cache from store. "
3386 "(" << StoreObjectInfoFile << ")");
3387 return false;
3388 }
3389
3390 //
3391 // check if we are loading some future format
3392 // version by mistake
3393 //
3394 int iVersion = 0;
3395 anArchive.Read(iVersion);
3396
3397 if(iVersion != STOREOBJECTINFO_VERSION)
3398 {
3399 BOX_WARNING("Store object info file "
3400 "version " << iVersion << " unsupported. "
3401 "Will re-cache from store. "
3402 "(" << StoreObjectInfoFile << ")");
3403 return false;
3404 }
3405
3406 //
3407 // check if this state file is even valid
3408 // for the loaded bbackupd.conf file
3409 //
3410 box_time_t lastKnownConfigModTime;
3411 anArchive.Read(lastKnownConfigModTime);
3412
3413 if(lastKnownConfigModTime != GetLoadedConfigModifiedTime())
3414 {
3415 BOX_WARNING("Store object info file "
3416 "out of date. Will re-cache from store. "
3417 "(" << StoreObjectInfoFile << ")");
3418 return false;
3419 }
3420
3421 //
3422 // this is it, go at it
3423 //
3424 anArchive.Read(mClientStoreMarker);
3425 anArchive.Read(theLastSyncTime);
3426 anArchive.Read(theNextSyncTime);
3427
3428 //
3429 //
3430 //
3431 int64_t iCount = 0;
3432 anArchive.Read(iCount);
3433
3434 for(int v = 0; v < iCount; v++)
3435 {
3436 Location* pLocation = new Location;
3437 if(!pLocation)
3438 {
3439 throw std::bad_alloc();
3440 }
3441
3442 pLocation->Deserialize(anArchive);
3443 mLocations.push_back(pLocation);
3444 }
3445
3446 //
3447 //
3448 //
3449 iCount = 0;
3450 anArchive.Read(iCount);
3451
3452 for(int v = 0; v < iCount; v++)
3453 {
3454 std::string strItem;
3455 anArchive.Read(strItem);
3456
3457 mIDMapMounts.push_back(strItem);
3458 }
3459
3460 //
3461 //
3462 //
3463 iCount = 0;
3464 anArchive.Read(iCount);
3465
3466 for(int v = 0; v < iCount; v++)
3467 {
3468 int64_t anId;
3469 anArchive.Read(anId);
3470
3471 std::string aName;
3472 anArchive.Read(aName);
3473
3474 mUnusedRootDirEntries.push_back(std::pair<int64_t, std::string>(anId, aName));
3475 }
3476
3477 if (iCount > 0)
3478 anArchive.Read(mDeleteUnusedRootDirEntriesAfter);
3479
3480 //
3481 //
3482 //
3483 aFile.Close();
3484 BOX_INFO("Loaded store object info file version " << iVersion
3485 << " (" << StoreObjectInfoFile << ")");
3486
3487 return true;
3488 }
3489 catch(std::exception &e)
3490 {
3491 BOX_ERROR("Internal error reading store object info file: "
3492 << StoreObjectInfoFile << ": " << e.what());
3493 }
3494 catch(...)
3495 {
3496 BOX_ERROR("Internal error reading store object info file: "
3497 << StoreObjectInfoFile << ": unknown error");
3498 }
3499
3500 DeleteAllLocations();
3501
3502 mClientStoreMarker = BackupClientContext::ClientStoreMarker_NotKnown;
3503 theLastSyncTime = 0;
3504 theNextSyncTime = 0;
3505
3506 BOX_WARNING("Store object info file is missing, not accessible, "
3507 "or inconsistent. Will re-cache from store. "
3508 "(" << StoreObjectInfoFile << ")");
3509
3510 return false;
3511 }
3512
3513 // --------------------------------------------------------------------------
3514 //
3515 // Function
3516 // Name: BackupDaemon::DeleteStoreObjectInfo()
3517 // Purpose: Deletes the serialised state file, to prevent us
3518 // from using it again if a backup is interrupted.
3519 //
3520 // Created: 2006/02/12
3521 //
3522 // --------------------------------------------------------------------------
3523
DeleteStoreObjectInfo() const3524 bool BackupDaemon::DeleteStoreObjectInfo() const
3525 {
3526 if(!GetConfiguration().KeyExists("StoreObjectInfoFile"))
3527 {
3528 return false;
3529 }
3530
3531 std::string storeObjectInfoFile(GetConfiguration().GetKeyValue("StoreObjectInfoFile"));
3532
3533 // Check to see if the file exists
3534 if(!FileExists(storeObjectInfoFile.c_str()))
3535 {
3536 // File doesn't exist -- so can't be deleted. But something
3537 // isn't quite right, so log a message
3538 BOX_WARNING("StoreObjectInfoFile did not exist when it "
3539 "was supposed to: " << storeObjectInfoFile);
3540
3541 // Return true to stop things going around in a loop
3542 return true;
3543 }
3544
3545 // Actually delete it
3546 if(::unlink(storeObjectInfoFile.c_str()) != 0)
3547 {
3548 BOX_LOG_SYS_ERROR("Failed to delete the old "
3549 "StoreObjectInfoFile: " << storeObjectInfoFile);
3550 return false;
3551 }
3552
3553 return true;
3554 }
3555