2 /// \file DeviceIface.cc
3 /// Interface class for device backup and restore
7 Copyright (C) 2007-2010, 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"
33 DeviceInterface::DeviceInterface(Device
*dev
)
37 , m_dbnameMutex(new Glib::Mutex
) // this is just in an effort to
38 // avoid gtkmm headers in
41 , m_tar_record_loaded(false)
42 , m_thread_quit(false)
46 DeviceInterface::~DeviceInterface()
52 bool DeviceInterface::False(const std::string
&msg
)
58 void DeviceInterface::BackupThread()
61 m_thread_quit
= false;
62 m_last_thread_error
= "";
65 // cycle through all database names in the dbList
67 Barry::ConfigFile::DBListType::const_iterator name
= m_dbList
.begin();
68 for( ; name
!= m_dbList
.end(); ++name
) {
69 // save current db name
70 SetThreadDBName(*name
);
71 // call the controller to do the work
72 unsigned int dbId
= m_desktop
->GetDBID(*name
);
73 m_desktop
->LoadDatabase(dbId
, *this);
76 catch( Glib::Exception
&e
) {
77 m_last_thread_error
= e
.what();
80 catch( std::exception
&e
) {
81 m_last_thread_error
= e
.what();
85 m_last_thread_error
= _("Terminated by user.");
92 m_AppComm
.m_error
->emit();
94 // signal host thread that we're done
95 m_AppComm
.m_done
->emit();
98 m_AppComm
.Invalidate();
101 void DeviceInterface::RestoreThread()
103 m_thread_quit
= false;
104 m_last_thread_error
= "";
108 // cycle until m_end_of_tar
109 while( !m_end_of_tar
) {
111 // call the controller to do the work
112 unsigned int dbId
= m_desktop
->GetDBID(m_current_dbname
);
113 m_AppComm
.m_erase_db
->emit();
114 m_desktop
->SaveDatabase(dbId
, *this);
115 m_AppComm
.m_restored_db
->emit();
117 catch( Barry::Error
&be
) {
119 m_last_thread_error
= _("Error while restoring ");
120 m_last_thread_error
+= m_current_dbname
+ ". ";
121 m_last_thread_error
+= be
.what();
122 m_last_thread_error
+= _(" Will continue processing.");
124 // notify host thread
125 m_AppComm
.m_error
->emit();
127 // skip over records from this db
128 std::cerr
<< _("Error on database: ")
129 << m_current_dbname
<< std::endl
;
135 catch( Glib::Exception
&e
) {
136 m_last_thread_error
= e
.what();
137 m_AppComm
.m_error
->emit();
139 catch( std::exception
&e
) {
140 m_last_thread_error
= e
.what();
141 m_AppComm
.m_error
->emit();
144 m_last_thread_error
= _("Terminated by user.");
150 // signal host thread that we're done
151 m_AppComm
.m_done
->emit();
154 m_AppComm
.Invalidate();
157 void DeviceInterface::RestoreAndBackupThread()
159 m_thread_quit
= false;
160 m_last_thread_error
= "";
164 // cycle until m_end_of_tar
165 while( !m_end_of_tar
) {
166 unsigned int dbId
= m_desktop
->GetDBID(m_current_dbname
);
171 m_AppComm
.m_erase_db
->emit();
172 m_desktop
->SaveDatabase(dbId
, *this);
175 catch( Barry::Error
&be
) {
177 m_last_thread_error
= _("Error while restoring ");
178 m_last_thread_error
+= m_current_dbname
+ ". ";
179 m_last_thread_error
+= be
.what();
180 m_last_thread_error
+= _(" Will continue processing.");
182 // notify host thread
183 m_AppComm
.m_error
->emit();
185 // skip over records from this db
186 std::cerr
<< _("Error on database: ")
187 << m_current_dbname
<< std::endl
;
191 // then the backup, even if restore fails
192 m_desktop
->LoadDatabase(dbId
, *this);
197 catch( Glib::Exception
&e
) {
198 m_last_thread_error
= e
.what();
199 m_AppComm
.m_error
->emit();
201 catch( std::exception
&e
) {
202 m_last_thread_error
= e
.what();
203 m_AppComm
.m_error
->emit();
206 m_last_thread_error
= _("Terminated by user.");
214 // signal host thread that we're done
215 m_AppComm
.m_done
->emit();
218 m_AppComm
.Invalidate();
221 std::string
DeviceInterface::MakeFilename(const std::string
&label
) const
223 time_t t
= time(NULL
);
224 struct tm
*lt
= localtime(&t
);
226 std::string fileLabel
= label
;
227 if( fileLabel
.size() ) {
229 fileLabel
.insert(fileLabel
.begin(), '-');
231 // change all spaces in label to underscores
232 for( size_t i
= 0; i
< fileLabel
.size(); i
++ ) {
233 if( fileLabel
[i
] == ' ' )
238 std::ostringstream tarfilename
;
239 tarfilename
<< m_dev
->GetPIN().str() << "-"
240 << std::setw(4) << std::setfill('0') << (lt
->tm_year
+ 1900)
241 << std::setw(2) << std::setfill('0') << (lt
->tm_mon
+ 1)
242 << std::setw(2) << std::setfill('0') << lt
->tm_mday
244 << std::setw(2) << std::setfill('0') << lt
->tm_hour
245 << std::setw(2) << std::setfill('0') << lt
->tm_min
246 << std::setw(2) << std::setfill('0') << lt
->tm_sec
249 return tarfilename
.str();
252 int DeviceInterface::CountFiles(reuse::TarFile
&tar
,
253 const Barry::ConfigFile::DBListType
&restoreList
) const
256 std::string name
, last_name
;
259 while( tar
.ReadNextFilenameOnly(name
) ) {
260 std::string::size_type pos
= name
.rfind('/');
261 if( pos
== std::string::npos
)
262 continue; // bad name
263 std::string dbname
= name
.substr(0, pos
);
265 if( dbname
!= last_name
) {
267 good
= restoreList
.IsSelected(dbname
);
275 /// Splits a tarpath of the form "DBName/DBID" into separate string values.
276 /// Returns true if successful, false if tarpath is a bad name.
277 bool DeviceInterface::SplitTarPath(const std::string
&tarpath
,
279 std::string
&dbid_text
,
281 uint32_t &dbid
) const
283 std::string::size_type pos
= tarpath
.rfind('/');
284 if( pos
== std::string::npos
)
285 return false; // bad name
287 dbname
= tarpath
.substr(0, pos
);
288 dbid_text
= tarpath
.substr(pos
+ 1);
289 if( dbname
.size() == 0 || dbid_text
.size() == 0 )
290 return false; // bad name
292 std::istringstream
iss(dbid_text
);
294 iss
>> std::hex
>> dbid
>> temp
;
295 dbrectype
= (uint8_t) temp
;
300 void DeviceInterface::SetThreadDBName(const std::string
&dbname
)
302 Glib::Mutex::Lock
lock(*m_dbnameMutex
);
303 m_current_dbname_not_thread_safe
= dbname
;
304 m_current_dbname
= dbname
;
307 //////////////////////////////////////////////////////////////////////////////
310 void DeviceInterface::Reset()
312 Usb::Device
dev(m_dev
->result
.m_dev
);
316 bool DeviceInterface::Connect()
320 m_con
= new Barry::Controller(m_dev
->result
);
321 m_desktop
= new Barry::Mode::Desktop(*m_con
);
325 catch( Barry::BadPassword
& ) {
326 // pass on to the caller
329 catch( Barry::BadSize
& ) {
330 // pass on to the caller
334 catch( Barry::Error
&e
) {
336 return False(e
.what());
340 bool DeviceInterface::Password(const char *password
)
343 m_desktop
->RetryPassword(password
);
346 catch( Barry::BadPassword
& ) {
347 // pass on to the caller
350 catch( Barry::Error
&e
) {
352 return False(e
.what());
356 void DeviceInterface::Disconnect()
365 // cycle through controller's DBDB and count the records in all the
366 // databases selected in the backupList
367 unsigned int DeviceInterface::GetRecordTotal(const Barry::ConfigFile::DBListType
&backupList
) const
369 unsigned int count
= 0;
371 Barry::DatabaseDatabase::DatabaseArrayType::const_iterator
372 i
= m_desktop
->GetDBDB().Databases
.begin();
373 for( ; i
!= m_desktop
->GetDBDB().Databases
.end(); ++i
) {
374 if( backupList
.IsSelected(i
->Name
) ) {
375 count
+= i
->RecordCount
;
381 unsigned int DeviceInterface::GetRecordTotal(const Barry::ConfigFile::DBListType
&restoreList
, const std::string
&filename
) const
383 unsigned int count
= 0;
385 std::auto_ptr
<reuse::TarFile
> tar
;
388 // do a scan through the tar file
389 tar
.reset( new reuse::TarFile(filename
.c_str(), false, &reuse::gztar_ops_nonthread
, true) );
390 count
= CountFiles(*tar
, restoreList
);
392 catch( reuse::TarFile::TarError
&te
) {
393 // just throw it away
398 /// returns name of database the thread is currently working on
399 std::string
DeviceInterface::GetThreadDBName() const
401 Glib::Mutex::Lock
lock(*m_dbnameMutex
);
402 return m_current_dbname_not_thread_safe
;
405 bool DeviceInterface::StartBackup(AppComm comm
,
406 const Barry::ConfigFile::DBListType
&backupList
,
407 const std::string
&directory
,
408 const std::string
&backupLabel
)
410 if( m_AppComm
.IsValid() )
411 return False(_("Thread already running."));
414 std::string filename
= directory
+ "/" + MakeFilename(backupLabel
);
415 m_tarback
.reset( new reuse::TarFile(filename
.c_str(), true, &reuse::gztar_ops_nonthread
, true) );
417 catch( reuse::TarFile::TarError
&te
) {
418 return False(te
.what());
423 m_dbList
= backupList
;
426 Glib::Thread::create(sigc::mem_fun(*this, &DeviceInterface::BackupThread
), false);
430 bool DeviceInterface::StartRestore(AppComm comm
,
431 const Barry::ConfigFile::DBListType
&restoreList
,
432 const std::string
&filename
)
434 if( m_AppComm
.IsValid() )
435 return False(_("Thread already running."));
438 // open for the main restore
439 m_tar
.reset( new reuse::TarFile(filename
.c_str(), false, &reuse::gztar_ops_nonthread
, true) );
442 catch( reuse::TarFile::TarError
&te
) {
443 return False(te
.what());
448 m_dbList
= restoreList
;
449 m_current_dbname_not_thread_safe
= "";
450 m_current_dbname
= "";
452 m_end_of_tar
= false;
453 m_tar_record_loaded
= false;
455 // get first tar record
459 Glib::Thread::create(sigc::mem_fun(*this, &DeviceInterface::RestoreThread
), false);
463 bool DeviceInterface::StartRestoreAndBackup(AppComm comm
,
464 const Barry::ConfigFile::DBListType
&restoreAndBackupList
,
465 const std::string
&filename
,
466 const std::string
&directory
)
468 if( m_AppComm
.IsValid() )
469 return False(_("Thread already running."));
472 // open for the main restore
473 m_tar
.reset( new reuse::TarFile(filename
.c_str(), false, &reuse::gztar_ops_nonthread
, true) );
475 // open for secondary backup
476 std::string back
= directory
+ "/" + MakeFilename(m_dev
->GetPIN().str());
477 m_tarback
.reset( new reuse::TarFile(back
.c_str(), true, &reuse::gztar_ops_nonthread
, true) );
480 catch( reuse::TarFile::TarError
&te
) {
481 return False(te
.what());
486 m_dbList
= restoreAndBackupList
;
487 m_current_dbname_not_thread_safe
= "";
488 m_current_dbname
= "";
490 m_end_of_tar
= false;
491 m_tar_record_loaded
= false;
493 // get first tar record
497 Glib::Thread::create(sigc::mem_fun(*this, &DeviceInterface::RestoreAndBackupThread
), false);
503 //////////////////////////////////////////////////////////////////////////////
504 // Barry::Parser overrides
506 void DeviceInterface::ParseRecord(const Barry::DBData
&data
,
507 const Barry::IConverter
*ic
)
509 m_rec_type
= data
.GetRecType();
510 m_unique_id
= data
.GetUniqueId();
511 std::ostringstream oss
;
512 oss
<< std::hex
<< m_unique_id
<< " " << (unsigned int)m_rec_type
;
513 m_tar_id_text
= oss
.str();
514 if( m_tar_id_text
.size() == 0 )
515 throw std::runtime_error(_("No unique ID available!"));
517 m_record_data
.assign(
518 (const char*)data
.GetData().GetData() + data
.GetOffset(),
519 data
.GetData().GetSize() - data
.GetOffset());
522 std::string tarname
= m_current_dbname
+ "/" + m_tar_id_text
;
523 m_tarback
->AppendFile(tarname
.c_str(), m_record_data
);
525 m_AppComm
.m_progress
->emit();
528 if( m_thread_quit
) {
534 //////////////////////////////////////////////////////////////////////////////
535 // Barry::Builder overrides
537 bool DeviceInterface::Retrieve()
542 // if loaded, we are likely on a database
543 // boundary, and the last read crossed it, so don't load again
544 if( m_tar_record_loaded
)
547 // search for a valid record
549 // load record data from tar file
550 std::string filename
;
551 if( !m_tar
->ReadNextFile(filename
, m_record_data
) ) {
552 // assume end of file
556 m_tar_record_loaded
= true;
558 // split record filename into dbname and ID
560 if( !SplitTarPath(filename
, dbname
, m_tar_id_text
, m_rec_type
, m_unique_id
) ) {
561 // invalid filename, skip it
562 std::cerr
<< _("Skipping invalid tar record: ") << filename
<< std::endl
;
566 // are we working on the same dbname as last time? if so, go ahead!
567 if( m_current_dbname
== dbname
) {
571 // DIFFERENT DBNAME from here on down!
573 // does the filter allow this record? if not, skip it and continue
575 if( !m_dbList
.IsSelected(dbname
) ) {
579 // all checks pass, load the new dbname, and return false
580 // if we are on a dbname boundary
582 if( m_current_dbname
.size() == 0 ) {
583 // this is the first time through Retrieve, so ok
587 SetThreadDBName(dbname
);
592 bool DeviceInterface::BuildRecord(Barry::DBData
&data
, size_t &offset
,
593 const Barry::IConverter
*ic
)
598 // fill in the meta data
599 data
.SetVersion(Barry::DBData::REC_VERSION_1
);
600 data
.SetDBName(m_current_dbname
);
601 data
.SetIds(m_rec_type
, m_unique_id
);
602 data
.SetOffset(offset
);
604 // fill in the record data
605 int packet_size
= offset
+ m_record_data
.size();
606 Barry::Data
&block
= data
.UseData();
607 unsigned char *buf
= block
.GetBuffer(packet_size
);
608 memcpy(buf
+ offset
, m_record_data
.data(), m_record_data
.size());
609 offset
+= m_record_data
.size();
610 block
.ReleaseBuffer(packet_size
);
612 // clear loaded flag, as it has now been used
613 m_tar_record_loaded
= false;
614 m_AppComm
.m_progress
->emit();
618 bool DeviceInterface::FetchRecord(Barry::DBData
&data
,
619 const Barry::IConverter
*ic
)
622 return BuildRecord(data
, offset
, ic
);
625 // helper function for halding restore errors
626 void DeviceInterface::SkipCurrentDB() throw()
628 // skip all records until next DB
630 while( Retrieve() ) {
631 std::cerr
<< _("Skipping: ")
632 << m_current_dbname
<< "/"
633 << m_tar_id_text
<< std::endl
;
634 m_tar_record_loaded
= false;
637 catch( reuse::TarFile::TarError
& ) {
641 // swallow all other exceptions
642 std::cerr
<< "EXCEPTION IN SkipCurrentDB()! "
643 "Please report to Barry mailing list." << std::endl
;
647 Device::Device(const Barry::ProbeResult
&result
)