source: box/trunk/bin/bbackupd/BackupClientContext.cpp @ 3090

Revision 3090, 14.5 KB checked in by chris, 6 weeks ago (diff)

Fix a memory leak when TcpNice? is disabled.

  • Property svn:eol-style set to native
Line 
1// --------------------------------------------------------------------------
2//
3// File
4//              Name:    BackupClientContext.cpp
5//              Purpose: Keep track of context
6//              Created: 2003/10/08
7//
8// --------------------------------------------------------------------------
9
10#include "Box.h"
11
12#ifdef HAVE_SIGNAL_H
13        #include <signal.h>
14#endif
15
16#ifdef HAVE_SYS_TIME_H
17        #include <sys/time.h>
18#endif
19
20#include "BoxPortsAndFiles.h"
21#include "BoxTime.h"
22#include "BackupClientContext.h"
23#include "SocketStreamTLS.h"
24#include "Socket.h"
25#include "BackupStoreConstants.h"
26#include "BackupStoreException.h"
27#include "BackupDaemon.h"
28#include "autogen_BackupProtocol.h"
29#include "BackupStoreFile.h"
30#include "Logging.h"
31#include "TcpNice.h"
32
33#include "MemLeakFindOn.h"
34
35// --------------------------------------------------------------------------
36//
37// Function
38//              Name:    BackupClientContext::BackupClientContext(BackupDaemon &, TLSContext &, const std::string &, int32_t, bool, bool, std::string)
39//              Purpose: Constructor
40//              Created: 2003/10/08
41//
42// --------------------------------------------------------------------------
43BackupClientContext::BackupClientContext
44(
45        LocationResolver &rResolver, 
46        TLSContext &rTLSContext, 
47        const std::string &rHostname,
48        int Port,
49        uint32_t AccountNumber, 
50        bool ExtendedLogging,
51        bool ExtendedLogToFile,
52        std::string ExtendedLogFile,
53        ProgressNotifier& rProgressNotifier,
54        bool TcpNiceMode
55)
56        : mrResolver(rResolver),
57          mrTLSContext(rTLSContext),
58          mHostname(rHostname),
59          mPort(Port),
60          mAccountNumber(AccountNumber),
61          mExtendedLogging(ExtendedLogging),
62          mExtendedLogToFile(ExtendedLogToFile),
63          mExtendedLogFile(ExtendedLogFile),
64          mpExtendedLogFileHandle(NULL),
65          mClientStoreMarker(ClientStoreMarker_NotKnown),
66          mpDeleteList(0),
67          mpCurrentIDMap(0),
68          mpNewIDMap(0),
69          mStorageLimitExceeded(false),
70          mpExcludeFiles(0),
71          mpExcludeDirs(0),
72          mKeepAliveTimer(0, "KeepAliveTime"),
73          mbIsManaged(false),
74          mrProgressNotifier(rProgressNotifier),
75          mTcpNiceMode(TcpNiceMode)
76{
77}
78
79// --------------------------------------------------------------------------
80//
81// Function
82//              Name:    BackupClientContext::~BackupClientContext()
83//              Purpose: Destructor
84//              Created: 2003/10/08
85//
86// --------------------------------------------------------------------------
87BackupClientContext::~BackupClientContext()
88{
89        CloseAnyOpenConnection();
90       
91        // Delete delete list
92        if(mpDeleteList != 0)
93        {
94                delete mpDeleteList;
95                mpDeleteList = 0;
96        }
97}
98
99// --------------------------------------------------------------------------
100//
101// Function
102//              Name:    BackupClientContext::GetConnection()
103//              Purpose: Returns the connection, making the connection and logging into
104//                               the backup store if necessary.
105//              Created: 2003/10/08
106//
107// --------------------------------------------------------------------------
108BackupProtocolClient &BackupClientContext::GetConnection()
109{
110        // Already got it? Just return it.
111        if(mapConnection.get())
112        {
113                return *mapConnection;
114        }
115       
116        // there shouldn't be a connection open
117        ASSERT(mapSocket.get() == 0);
118        // Defensive. Must close connection before releasing any old socket.
119        mapConnection.reset();
120        mapSocket.reset(new SocketStreamTLS);
121       
122        try
123        {
124                // Defensive.
125                mapConnection.reset();
126               
127                // Log intention
128                BOX_INFO("Opening connection to server '" <<
129                        mHostname << "'...");
130
131                // Connect!
132                ((SocketStreamTLS *)(mapSocket.get()))->Open(mrTLSContext,
133                        Socket::TypeINET, mHostname, mPort);
134               
135                if(mTcpNiceMode)
136                {
137                        // Pass control of mapSocket to NiceSocketStream,
138                        // which will take care of destroying it for us.
139                        mapNice.reset(new NiceSocketStream(mapSocket));
140                        mapConnection.reset(new BackupProtocolClient(*mapNice));
141                }
142                else
143                {
144                        mapConnection.reset(new BackupProtocolClient(*mapSocket));
145                }
146               
147                // Set logging option
148                mapConnection->SetLogToSysLog(mExtendedLogging);
149               
150                if (mExtendedLogToFile)
151                {
152                        ASSERT(mpExtendedLogFileHandle == NULL);
153                       
154                        mpExtendedLogFileHandle = fopen(
155                                mExtendedLogFile.c_str(), "a+");
156
157                        if (!mpExtendedLogFileHandle)
158                        {
159                                BOX_LOG_SYS_ERROR("Failed to open extended "
160                                        "log file: " << mExtendedLogFile);
161                        }
162                        else
163                        {
164                                mapConnection->SetLogToFile(mpExtendedLogFileHandle);
165                        }
166                }
167               
168                // Handshake
169                mapConnection->Handshake();
170               
171                // Check the version of the server
172                {
173                        std::auto_ptr<BackupProtocolVersion> serverVersion(
174                                mapConnection->QueryVersion(BACKUP_STORE_SERVER_VERSION));
175                        if(serverVersion->GetVersion() != BACKUP_STORE_SERVER_VERSION)
176                        {
177                                THROW_EXCEPTION(BackupStoreException, WrongServerVersion)
178                        }
179                }
180
181                // Login -- if this fails, the Protocol will exception
182                std::auto_ptr<BackupProtocolLoginConfirmed> loginConf(
183                        mapConnection->QueryLogin(mAccountNumber, 0 /* read/write */));
184               
185                // Check that the client store marker is the one we expect
186                if(mClientStoreMarker != ClientStoreMarker_NotKnown)
187                {
188                        if(loginConf->GetClientStoreMarker() != mClientStoreMarker)
189                        {
190                                // Not good... finish the connection, abort, etc, ignoring errors
191                                try
192                                {
193                                        mapConnection->QueryFinished();
194                                        mapNice.reset();
195                                        mapSocket.reset();
196                                }
197                                catch(...)
198                                {
199                                        // IGNORE
200                                }
201                               
202                                // Then throw an exception about this
203                                THROW_EXCEPTION(BackupStoreException, ClientMarkerNotAsExpected)
204                        }
205                }
206               
207                // Log success
208                BOX_INFO("Connection made, login successful");
209
210                // Check to see if there is any space available on the server
211                if(loginConf->GetBlocksUsed() >= loginConf->GetBlocksHardLimit())
212                {
213                        // no -- flag so only things like deletions happen
214                        mStorageLimitExceeded = true;
215                        // Log
216                        BOX_WARNING("Exceeded storage hard-limit on server, "
217                                "not uploading changes to files");
218                }
219        }
220        catch(...)
221        {
222                // Clean up.
223                mapConnection.reset();
224                mapNice.reset();
225                mapSocket.reset();
226                throw;
227        }
228       
229        return *mapConnection;
230}
231
232// --------------------------------------------------------------------------
233//
234// Function
235//              Name:    BackupClientContext::CloseAnyOpenConnection()
236//              Purpose: Closes a connection, if it's open
237//              Created: 2003/10/08
238//
239// --------------------------------------------------------------------------
240void BackupClientContext::CloseAnyOpenConnection()
241{
242        if(mapConnection.get())
243        {
244                try
245                {
246                        // Need to set a client store marker?
247                        if(mClientStoreMarker == ClientStoreMarker_NotKnown)
248                        {
249                                // Yes, choose one, the current time will do
250                                box_time_t marker = GetCurrentBoxTime();
251                               
252                                // Set it on the store
253                                mapConnection->QuerySetClientStoreMarker(marker);
254                               
255                                // Record it so that it can be picked up later.
256                                mClientStoreMarker = marker;
257                        }
258               
259                        // Quit nicely
260                        mapConnection->QueryFinished();
261                }
262                catch(...)
263                {
264                        // Ignore errors here
265                }
266               
267                // Delete it anyway.
268                mapConnection.reset();
269        }
270
271        try
272        {
273                // Be nice about closing the socket
274                mapNice.reset();
275                mapSocket.reset();
276        }
277        catch(...)
278        {
279                // Ignore errors
280        }
281
282        // Delete any pending list
283        if(mpDeleteList != 0)
284        {
285                delete mpDeleteList;
286                mpDeleteList = 0;
287        }
288
289        if (mpExtendedLogFileHandle != NULL)
290        {
291                fclose(mpExtendedLogFileHandle);
292                mpExtendedLogFileHandle = NULL;
293        }
294}
295
296
297
298// --------------------------------------------------------------------------
299//
300// Function
301//              Name:    BackupClientContext::GetTimeout()
302//              Purpose: Gets the current timeout time.
303//              Created: 2003/10/08
304//
305// --------------------------------------------------------------------------
306int BackupClientContext::GetTimeout() const
307{
308        if(mapConnection.get())
309        {
310                return mapConnection->GetTimeout();
311        }
312       
313        return (15*60*1000);
314}
315
316
317// --------------------------------------------------------------------------
318//
319// Function
320//              Name:    BackupClientContext::GetDeleteList()
321//              Purpose: Returns the delete list, creating one if necessary
322//              Created: 10/11/03
323//
324// --------------------------------------------------------------------------
325BackupClientDeleteList &BackupClientContext::GetDeleteList()
326{
327        // Already created?
328        if(mpDeleteList == 0)
329        {
330                mpDeleteList = new BackupClientDeleteList;
331        }
332
333        // Return reference to object   
334        return *mpDeleteList;
335}
336
337
338// --------------------------------------------------------------------------
339//
340// Function
341//              Name:    BackupClientContext::PerformDeletions()
342//              Purpose: Perform any pending file deletions.
343//              Created: 10/11/03
344//
345// --------------------------------------------------------------------------
346void BackupClientContext::PerformDeletions()
347{
348        // Got a list?
349        if(mpDeleteList == 0)
350        {
351                // Nothing to do
352                return;
353        }
354       
355        // Delegate to the delete list object
356        mpDeleteList->PerformDeletions(*this);
357       
358        // Delete the object
359        delete mpDeleteList;
360        mpDeleteList = 0;
361}
362
363
364
365// --------------------------------------------------------------------------
366//
367// Function
368//              Name:    BackupClientContext::GetCurrentIDMap() const
369//              Purpose: Return a (const) reference to the current ID map
370//              Created: 11/11/03
371//
372// --------------------------------------------------------------------------
373const BackupClientInodeToIDMap &BackupClientContext::GetCurrentIDMap() const
374{
375        ASSERT(mpCurrentIDMap != 0);
376        if(mpCurrentIDMap == 0)
377        {
378                THROW_EXCEPTION(CommonException, Internal)
379        }
380        return *mpCurrentIDMap;
381}
382
383// --------------------------------------------------------------------------
384//
385// Function
386//              Name:    BackupClientContext::GetNewIDMap() const
387//              Purpose: Return a reference to the new ID map
388//              Created: 11/11/03
389//
390// --------------------------------------------------------------------------
391BackupClientInodeToIDMap &BackupClientContext::GetNewIDMap() const
392{
393        ASSERT(mpNewIDMap != 0);
394        if(mpNewIDMap == 0)
395        {
396                THROW_EXCEPTION(CommonException, Internal)
397        }
398        return *mpNewIDMap;
399}
400
401
402// --------------------------------------------------------------------------
403//
404// Function
405//              Name:    BackupClientContext::FindFilename(int64_t, int64_t, std::string &, bool &) const
406//              Purpose: Attempts to find the pathname of an object with a given ID on the server.
407//                               Returns true if it can be found, in which case rPathOut is the local filename,
408//                               and rIsDirectoryOut == true if the local object is a directory.
409//              Created: 12/11/03
410//
411// --------------------------------------------------------------------------
412bool BackupClientContext::FindFilename(int64_t ObjectID, int64_t ContainingDirectory, std::string &rPathOut, bool &rIsDirectoryOut,
413        bool &rIsCurrentVersionOut, box_time_t *pModTimeOnServer, box_time_t *pAttributesHashOnServer, BackupStoreFilenameClear *pLeafname)
414{
415        // Make a connection to the server
416        BackupProtocolClient &connection(GetConnection());
417
418        // Request filenames from the server, in a "safe" manner to ignore errors properly
419        {
420                BackupProtocolGetObjectName send(ObjectID, ContainingDirectory);
421                connection.Send(send);
422        }
423        std::auto_ptr<BackupProtocolMessage> preply(connection.Receive());
424
425        // Is it of the right type?
426        if(preply->GetType() != BackupProtocolObjectName::TypeID)
427        {
428                // Was an error or something
429                return false;
430        }
431
432        // Cast to expected type.
433        BackupProtocolObjectName *names = (BackupProtocolObjectName *)(preply.get());
434
435        // Anything found?
436        int32_t numElements = names->GetNumNameElements();
437        if(numElements <= 0)
438        {
439                // No.
440                return false;
441        }
442       
443        // Get the stream containing all the names
444        std::auto_ptr<IOStream> nameStream(connection.ReceiveStream());
445
446        // Path
447        std::string path;
448
449        // Remember this is in reverse order!
450        for(int l = 0; l < numElements; ++l)
451        {
452                BackupStoreFilenameClear elementName;
453                elementName.ReadFromStream(*nameStream, GetTimeout());
454
455                // Store leafname for caller?
456                if(l == 0 && pLeafname)
457                {
458                        *pLeafname = elementName;
459                }
460
461                // Is it part of the filename in the location?
462                if(l < (numElements - 1))
463                {
464                        // Part of filename within
465                        path = (path.empty())?(elementName.GetClearFilename()):(elementName.GetClearFilename() + DIRECTORY_SEPARATOR_ASCHAR + path);
466                }
467                else
468                {
469                        // Location name -- look up in daemon's records
470                        std::string locPath;
471                        if(!mrResolver.FindLocationPathName(elementName.GetClearFilename(), locPath))
472                        {
473                                // Didn't find the location... so can't give the local filename
474                                return false;
475                        }
476
477                        // Add in location path
478                        path = (path.empty())?(locPath):(locPath + DIRECTORY_SEPARATOR_ASCHAR + path);
479                }
480        }
481
482        // Is it a directory?
483        rIsDirectoryOut = ((names->GetFlags() & BackupProtocolListDirectory::Flags_Dir) == BackupProtocolListDirectory::Flags_Dir);
484       
485        // Is it the current version?
486        rIsCurrentVersionOut = ((names->GetFlags() & (BackupProtocolListDirectory::Flags_OldVersion | BackupProtocolListDirectory::Flags_Deleted)) == 0);
487
488        // And other information which may be required
489        if(pModTimeOnServer) *pModTimeOnServer = names->GetModificationTime();
490        if(pAttributesHashOnServer) *pAttributesHashOnServer = names->GetAttributesHash();
491
492        // Tell caller about the pathname
493        rPathOut = path;
494
495        // Found
496        return true;
497}
498
499void BackupClientContext::SetMaximumDiffingTime(int iSeconds)
500{
501        mMaximumDiffingTime = iSeconds < 0 ? 0 : iSeconds;
502        BOX_TRACE("Set maximum diffing time to " << mMaximumDiffingTime <<
503                " seconds");
504}
505
506void BackupClientContext::SetKeepAliveTime(int iSeconds)
507{
508        mKeepAliveTime = iSeconds < 0 ? 0 : iSeconds;
509        BOX_TRACE("Set keep-alive time to " << mKeepAliveTime << " seconds");
510        mKeepAliveTimer = Timer(mKeepAliveTime * 1000, "KeepAliveTime");
511}
512
513// --------------------------------------------------------------------------
514//
515// Function
516//              Name:    BackupClientContext::ManageDiffProcess()
517//              Purpose: Initiates a file diff control timer
518//              Created: 04/19/2005
519//
520// --------------------------------------------------------------------------
521void BackupClientContext::ManageDiffProcess()
522{
523        ASSERT(!mbIsManaged);
524        mbIsManaged = true;
525}
526
527// --------------------------------------------------------------------------
528//
529// Function
530//              Name:    BackupClientContext::UnManageDiffProcess()
531//              Purpose: suspends file diff control timer
532//              Created: 04/19/2005
533//
534// --------------------------------------------------------------------------
535void BackupClientContext::UnManageDiffProcess()
536{
537        // ASSERT(mbIsManaged);
538        mbIsManaged = false;
539}
540
541// --------------------------------------------------------------------------
542//
543// Function
544//              Name:    BackupClientContext::DoKeepAlive()
545//              Purpose: Check whether it's time to send a KeepAlive
546//                       message over the SSL link, and if so, send it.
547//              Created: 04/19/2005
548//
549// --------------------------------------------------------------------------
550void BackupClientContext::DoKeepAlive()
551{
552        if (!mapConnection.get())
553        {
554                return;
555        }
556       
557        if (mKeepAliveTime == 0)
558        {
559                return;
560        }
561
562        if (!mKeepAliveTimer.HasExpired())
563        {
564                return;
565        }
566       
567        BOX_TRACE("KeepAliveTime reached, sending keep-alive message");
568        mapConnection->QueryGetIsAlive();
569       
570        mKeepAliveTimer = Timer(mKeepAliveTime * 1000, "KeepAliveTime");
571}
572
573int BackupClientContext::GetMaximumDiffingTime() 
574{
575        return mMaximumDiffingTime;
576}
Note: See TracBrowser for help on using the repository browser.