2 /// \file DeviceIface.cc
3 /// Interface class for device backup and restore
7 Copyright (C) 2007-2008, Net Direct Inc. (http://www.netdirect.ca/)
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU General Public License in the COPYING file at the
19 root directory of this project for more details.
22 #include "DeviceIface.h"
31 DeviceInterface::DeviceInterface()
33 , m_dbnameMutex(new Glib::Mutex
) // this is just in an effort to
34 // avoid gtkmm headers in
37 , m_tar_record_loaded(false)
38 , m_thread_quit(false)
42 DeviceInterface::~DeviceInterface()
48 bool DeviceInterface::False(const std::string
&msg
)
54 void DeviceInterface::BackupThread()
57 m_thread_quit
= false;
58 m_last_thread_error
= "";
62 // cycle through all database names in the dbList
64 ConfigFile::DBListType::const_iterator name
= m_dbList
.begin();
65 for( ; name
!= m_dbList
.end(); ++name
) {
66 // save current db name
67 SetThreadDBName(*name
);
69 // call the controller to do the work
70 unsigned int dbId
= m_con
->GetDBID(*name
);
71 m_con
->LoadDatabase(dbId
, *this);
75 catch( Glib::Exception
&e
) {
76 m_last_thread_error
= e
.what();
79 catch( std::exception
&e
) {
80 m_last_thread_error
= e
.what();
84 m_last_thread_error
= "Terminated by user.";
91 m_AppComm
.m_error
->emit();
93 // signal host thread that we're done
94 m_AppComm
.m_done
->emit();
97 m_AppComm
.Invalidate();
100 void DeviceInterface::RestoreThread()
102 m_thread_quit
= false;
103 m_last_thread_error
= "";
107 // cycle until m_end_of_tar
108 while( !m_end_of_tar
) {
110 // call the controller to do the work
111 unsigned int dbId
= m_con
->GetDBID(m_current_dbname
);
112 m_AppComm
.m_erase_db
->emit();
113 m_con
->SaveDatabase(dbId
, *this);
115 catch( Barry::Error
&be
) {
117 m_last_thread_error
= "Error while restoring ";
118 m_last_thread_error
+= m_current_dbname
+ ". ";
119 m_last_thread_error
+= be
.what();
120 m_last_thread_error
+= " Will continue processing.";
122 // notify host thread
123 m_AppComm
.m_error
->emit();
125 // skip over records from this db
126 std::cerr
<< "Error on database: "
127 << m_current_dbname
<< std::endl
;
133 catch( Glib::Exception
&e
) {
134 m_last_thread_error
= e
.what();
135 m_AppComm
.m_error
->emit();
137 catch( std::exception
&e
) {
138 m_last_thread_error
= e
.what();
139 m_AppComm
.m_error
->emit();
142 m_last_thread_error
= "Terminated by user.";
148 // signal host thread that we're done
149 m_AppComm
.m_done
->emit();
152 m_AppComm
.Invalidate();
155 void DeviceInterface::RestoreAndBackupThread()
157 m_thread_quit
= false;
158 m_last_thread_error
= "";
162 // cycle until m_end_of_tar
163 while( !m_end_of_tar
) {
164 unsigned int dbId
= m_con
->GetDBID(m_current_dbname
);
169 m_AppComm
.m_erase_db
->emit();
170 m_con
->SaveDatabase(dbId
, *this);
173 catch( Barry::Error
&be
) {
175 m_last_thread_error
= "Error while restoring ";
176 m_last_thread_error
+= m_current_dbname
+ ". ";
177 m_last_thread_error
+= be
.what();
178 m_last_thread_error
+= " Will continue processing.";
180 // notify host thread
181 m_AppComm
.m_error
->emit();
183 // skip over records from this db
184 std::cerr
<< "Error on database: "
185 << m_current_dbname
<< std::endl
;
189 // then the backup, even if restore fails
190 m_con
->LoadDatabase(dbId
, *this);
195 catch( Glib::Exception
&e
) {
196 m_last_thread_error
= e
.what();
197 m_AppComm
.m_error
->emit();
199 catch( std::exception
&e
) {
200 m_last_thread_error
= e
.what();
201 m_AppComm
.m_error
->emit();
204 m_last_thread_error
= "Terminated by user.";
212 // signal host thread that we're done
213 m_AppComm
.m_done
->emit();
216 m_AppComm
.Invalidate();
219 std::string
DeviceInterface::MakeFilename(const std::string
&pin
)
221 time_t t
= time(NULL
);
222 struct tm
*lt
= localtime(&t
);
224 std::ostringstream tarfilename
;
225 tarfilename
<< pin
<< "-"
226 << std::setw(4) << std::setfill('0') << (lt
->tm_year
+ 1900)
227 << std::setw(2) << std::setfill('0') << (lt
->tm_mon
+ 1)
228 << std::setw(2) << std::setfill('0') << lt
->tm_mday
230 << std::setw(2) << std::setfill('0') << lt
->tm_hour
231 << std::setw(2) << std::setfill('0') << lt
->tm_min
232 << std::setw(2) << std::setfill('0') << lt
->tm_sec
234 return tarfilename
.str();
237 int DeviceInterface::CountFiles(reuse::TarFile
&tar
,
238 const ConfigFile::DBListType
&restoreList
)
241 std::string name
, last_name
;
244 while( tar
.ReadNextFilenameOnly(name
) ) {
245 std::string::size_type pos
= name
.rfind('/');
246 if( pos
== std::string::npos
)
247 continue; // bad name
248 std::string dbname
= name
.substr(0, pos
);
250 if( dbname
!= last_name
) {
252 good
= restoreList
.IsSelected(dbname
);
260 /// Splits a tarpath of the form "DBName/DBID" into separate string values.
261 /// Returns true if successful, false if tarpath is a bad name.
262 bool DeviceInterface::SplitTarPath(const std::string
&tarpath
,
264 std::string
&dbid_text
,
268 std::string::size_type pos
= tarpath
.rfind('/');
269 if( pos
== std::string::npos
)
270 return false; // bad name
272 dbname
= tarpath
.substr(0, pos
);
273 dbid_text
= tarpath
.substr(pos
+ 1);
274 if( dbname
.size() == 0 || dbid_text
.size() == 0 )
275 return false; // bad name
277 std::istringstream
iss(dbid_text
);
279 iss
>> std::hex
>> dbid
>> temp
;
280 dbrectype
= (uint8_t) temp
;
285 void DeviceInterface::SetThreadDBName(const std::string
&dbname
)
287 Glib::Mutex::Lock
lock(*m_dbnameMutex
);
288 m_current_dbname_not_thread_safe
= dbname
;
289 m_current_dbname
= dbname
;
293 //////////////////////////////////////////////////////////////////////////////
296 bool DeviceInterface::Connect(const Barry::ProbeResult
&dev
)
300 m_con
= new Barry::Controller(dev
);
301 m_con
->OpenMode(Barry::Controller::Desktop
);
304 catch( Barry::BadPassword
& ) {
305 // pass on to the caller
308 catch( Barry::BadSize
& ) {
309 // pass on to the caller
313 catch( Barry::Error
&e
) {
315 return False(e
.what());
319 bool DeviceInterface::Password(const char *password
)
322 m_con
->RetryPassword(password
);
325 catch( Barry::BadPassword
& ) {
326 // pass on to the caller
329 catch( Barry::Error
&e
) {
331 return False(e
.what());
335 void DeviceInterface::Disconnect()
341 // cycle through controller's DBDB and count the records in all the
342 // databases selected in the backupList
343 int DeviceInterface::GetDeviceRecordTotal(const ConfigFile::DBListType
&backupList
) const
347 Barry::DatabaseDatabase::DatabaseArrayType::const_iterator
348 i
= m_con
->GetDBDB().Databases
.begin();
349 for( ; i
!= m_con
->GetDBDB().Databases
.end(); ++i
) {
350 if( backupList
.IsSelected(i
->Name
) ) {
351 count
+= i
->RecordCount
;
357 /// returns name of database the thread is currently working on
358 std::string
DeviceInterface::GetThreadDBName() const
360 Glib::Mutex::Lock
lock(*m_dbnameMutex
);
361 return m_current_dbname_not_thread_safe
;
364 bool DeviceInterface::StartBackup(AppComm comm
,
365 const ConfigFile::DBListType
&backupList
,
366 const std::string
&directory
,
367 const std::string
&pin
)
369 if( m_AppComm
.IsValid() )
370 return False("Thread already running.");
374 std::string filename
= directory
+ "/" + MakeFilename(pin
);
375 m_tarback
.reset( new reuse::TarFile(filename
.c_str(), true, &reuse::gztar_ops_nonthread
, true) );
378 catch( reuse::TarFile::TarError
&te
) {
379 return False(te
.what());
384 m_dbList
= backupList
;
387 Glib::Thread::create(sigc::mem_fun(*this, &DeviceInterface::BackupThread
), false);
391 bool DeviceInterface::StartRestore(AppComm comm
,
392 const ConfigFile::DBListType
&restoreList
,
393 const std::string
&filename
,
396 if( m_AppComm
.IsValid() )
397 return False("Thread already running.");
401 // caller is asking for a total, so we do a quick
402 // scan through the tar file first
403 m_tar
.reset( new reuse::TarFile(filename
.c_str(), false, &reuse::gztar_ops_nonthread
, true) );
404 *pRecordCount
= CountFiles(*m_tar
, restoreList
);
406 // close for next open
410 // open for the main restore
411 m_tar
.reset( new reuse::TarFile(filename
.c_str(), false, &reuse::gztar_ops_nonthread
, true) );
414 catch( reuse::TarFile::TarError
&te
) {
415 return False(te
.what());
420 m_dbList
= restoreList
;
421 m_current_dbname_not_thread_safe
= "";
422 m_current_dbname
= "";
424 m_end_of_tar
= false;
425 m_tar_record_loaded
= false;
427 // get first tar record
431 Glib::Thread::create(sigc::mem_fun(*this, &DeviceInterface::RestoreThread
), false);
435 bool DeviceInterface::StartRestoreAndBackup(AppComm comm
,
436 const ConfigFile::DBListType
&restoreAndBackupList
,
437 const std::string
&filename
,
438 const std::string
&directory
, const std::string
&pin
,
441 if( m_AppComm
.IsValid() )
442 return False("Thread already running.");
446 // caller is asking for a total, so we do a quick
447 // scan through the tar file first
448 m_tar
.reset( new reuse::TarFile(filename
.c_str(), false, &reuse::gztar_ops_nonthread
, true) );
449 *pRecordCount
= CountFiles(*m_tar
, restoreAndBackupList
);
451 // close for next open
455 // open for the main restore
456 m_tar
.reset( new reuse::TarFile(filename
.c_str(), false, &reuse::gztar_ops_nonthread
, true) );
458 // open for secondary backup
459 std::string back
= directory
+ "/" + MakeFilename(pin
);
460 m_tarback
.reset( new reuse::TarFile(back
.c_str(), true, &reuse::gztar_ops_nonthread
, true) );
463 catch( reuse::TarFile::TarError
&te
) {
464 return False(te
.what());
469 m_dbList
= restoreAndBackupList
;
470 m_current_dbname_not_thread_safe
= "";
471 m_current_dbname
= "";
473 m_end_of_tar
= false;
474 m_tar_record_loaded
= false;
476 // get first tar record
480 Glib::Thread::create(sigc::mem_fun(*this, &DeviceInterface::RestoreAndBackupThread
), false);
486 //////////////////////////////////////////////////////////////////////////////
487 // Barry::Parser overrides
489 void DeviceInterface::SetIds(uint8_t RecType
, uint32_t UniqueId
)
491 m_rec_type
= RecType
;
492 m_unique_id
= UniqueId
;
493 std::ostringstream oss
;
494 oss
<< std::hex
<< m_unique_id
<< " " << (unsigned int)m_rec_type
;
495 m_tar_id_text
= oss
.str();
496 if( m_tar_id_text
.size() == 0 )
497 throw std::runtime_error("No unique ID available!");
500 void DeviceInterface::ParseFields(const Barry::Data
&data
, size_t &offset
)
502 m_record_data
.assign((const char*)data
.GetData() + offset
, data
.GetSize() - offset
);
505 void DeviceInterface::Store()
507 std::string tarname
= m_current_dbname
+ "/" + m_tar_id_text
;
508 m_tarback
->AppendFile(tarname
.c_str(), m_record_data
);
510 m_AppComm
.m_progress
->emit();
513 if( m_thread_quit
) {
519 //////////////////////////////////////////////////////////////////////////////
520 // Barry::Builder overrides
522 bool DeviceInterface::Retrieve(unsigned int dbId
)
527 // if loaded, we are likely on a database
528 // boundary, and the last read crossed it, so don't load again
529 if( m_tar_record_loaded
)
532 // search for a valid record
534 // load record data from tar file
535 std::string filename
;
536 if( !m_tar
->ReadNextFile(filename
, m_record_data
) ) {
537 // assume end of file
541 m_tar_record_loaded
= true;
543 // split record filename into dbname and ID
545 if( !SplitTarPath(filename
, dbname
, m_tar_id_text
, m_rec_type
, m_unique_id
) ) {
546 // invalid filename, skip it
547 std::cerr
<< "Skipping invalid tar record: " << filename
<< std::endl
;
551 // are we working on the same dbname as last time? if so, go ahead!
552 if( m_current_dbname
== dbname
) {
556 // DIFFERENT DBNAME from here on down!
558 // does the filter allow this record? if not, skip it and continue
560 if( !m_dbList
.IsSelected(dbname
) ) {
564 // all checks pass, load the new dbname, and return false
565 // if we are on a dbname boundary
567 if( m_current_dbname
.size() == 0 ) {
568 // this is the first time through Retrieve, so ok
572 SetThreadDBName(dbname
);
577 uint8_t DeviceInterface::GetRecType() const
582 uint32_t DeviceInterface::GetUniqueId() const
587 void DeviceInterface::BuildHeader(Barry::Data
&data
, size_t &offset
)
592 void DeviceInterface::BuildFields(Barry::Data
&data
, size_t &offset
)
594 int packet_size
= offset
+ m_record_data
.size();
595 unsigned char *buf
= data
.GetBuffer(packet_size
);
596 memcpy(buf
+ offset
, m_record_data
.data(), m_record_data
.size());
597 offset
+= m_record_data
.size();
598 data
.ReleaseBuffer(packet_size
);
600 // clear loaded flag, as it has now been used
601 m_tar_record_loaded
= false;
603 m_AppComm
.m_progress
->emit();
606 // helper function for halding restore errors
607 void DeviceInterface::SkipCurrentDB() throw()
609 // skip all records until next DB
611 while( Retrieve(0) ) {
612 std::cerr
<< "Skipping: "
613 << m_current_dbname
<< "/"
614 << m_tar_id_text
<< std::endl
;
615 m_tar_record_loaded
= false;
618 catch( reuse::TarFile::TarError
& ) {
622 // swallow all other exceptions
623 std::cerr
<< "EXCEPTION IN SkipCurrentDB()! "
624 "Please report to Barry mailing list." << std::endl
;