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"
32 DeviceInterface::DeviceInterface(Device
*dev
)
36 , m_dbnameMutex(new Glib::Mutex
) // this is just in an effort to
37 // avoid gtkmm headers in
40 , m_tar_record_loaded(false)
41 , m_thread_quit(false)
45 DeviceInterface::~DeviceInterface()
51 bool DeviceInterface::False(const std::string
&msg
)
57 void DeviceInterface::BackupThread()
60 m_thread_quit
= false;
61 m_last_thread_error
= "";
64 // cycle through all database names in the dbList
66 Barry::ConfigFile::DBListType::const_iterator name
= m_dbList
.begin();
67 for( ; name
!= m_dbList
.end(); ++name
) {
68 // save current db name
69 SetThreadDBName(*name
);
70 // call the controller to do the work
71 unsigned int dbId
= m_desktop
->GetDBID(*name
);
72 m_desktop
->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_desktop
->GetDBID(m_current_dbname
);
112 m_AppComm
.m_erase_db
->emit();
113 m_desktop
->SaveDatabase(dbId
, *this);
114 m_AppComm
.m_restored_db
->emit();
116 catch( Barry::Error
&be
) {
118 m_last_thread_error
= "Error while restoring ";
119 m_last_thread_error
+= m_current_dbname
+ ". ";
120 m_last_thread_error
+= be
.what();
121 m_last_thread_error
+= " Will continue processing.";
123 // notify host thread
124 m_AppComm
.m_error
->emit();
126 // skip over records from this db
127 std::cerr
<< "Error on database: "
128 << m_current_dbname
<< std::endl
;
134 catch( Glib::Exception
&e
) {
135 m_last_thread_error
= e
.what();
136 m_AppComm
.m_error
->emit();
138 catch( std::exception
&e
) {
139 m_last_thread_error
= e
.what();
140 m_AppComm
.m_error
->emit();
143 m_last_thread_error
= "Terminated by user.";
149 // signal host thread that we're done
150 m_AppComm
.m_done
->emit();
153 m_AppComm
.Invalidate();
156 void DeviceInterface::RestoreAndBackupThread()
158 m_thread_quit
= false;
159 m_last_thread_error
= "";
163 // cycle until m_end_of_tar
164 while( !m_end_of_tar
) {
165 unsigned int dbId
= m_desktop
->GetDBID(m_current_dbname
);
170 m_AppComm
.m_erase_db
->emit();
171 m_desktop
->SaveDatabase(dbId
, *this);
174 catch( Barry::Error
&be
) {
176 m_last_thread_error
= "Error while restoring ";
177 m_last_thread_error
+= m_current_dbname
+ ". ";
178 m_last_thread_error
+= be
.what();
179 m_last_thread_error
+= " Will continue processing.";
181 // notify host thread
182 m_AppComm
.m_error
->emit();
184 // skip over records from this db
185 std::cerr
<< "Error on database: "
186 << m_current_dbname
<< std::endl
;
190 // then the backup, even if restore fails
191 m_desktop
->LoadDatabase(dbId
, *this);
196 catch( Glib::Exception
&e
) {
197 m_last_thread_error
= e
.what();
198 m_AppComm
.m_error
->emit();
200 catch( std::exception
&e
) {
201 m_last_thread_error
= e
.what();
202 m_AppComm
.m_error
->emit();
205 m_last_thread_error
= "Terminated by user.";
213 // signal host thread that we're done
214 m_AppComm
.m_done
->emit();
217 m_AppComm
.Invalidate();
220 std::string
DeviceInterface::MakeFilename(const std::string
&label
) const
222 time_t t
= time(NULL
);
223 struct tm
*lt
= localtime(&t
);
225 std::string fileLabel
= label
;
226 if( fileLabel
.size() ) {
228 fileLabel
.insert(fileLabel
.begin(), '-');
230 // change all spaces in label to underscores
231 for( size_t i
= 0; i
< fileLabel
.size(); i
++ ) {
232 if( fileLabel
[i
] == ' ' )
237 std::ostringstream tarfilename
;
238 tarfilename
<< m_dev
->GetPIN().str() << "-"
239 << std::setw(4) << std::setfill('0') << (lt
->tm_year
+ 1900)
240 << std::setw(2) << std::setfill('0') << (lt
->tm_mon
+ 1)
241 << std::setw(2) << std::setfill('0') << lt
->tm_mday
243 << std::setw(2) << std::setfill('0') << lt
->tm_hour
244 << std::setw(2) << std::setfill('0') << lt
->tm_min
245 << std::setw(2) << std::setfill('0') << lt
->tm_sec
248 return tarfilename
.str();
251 int DeviceInterface::CountFiles(reuse::TarFile
&tar
,
252 const Barry::ConfigFile::DBListType
&restoreList
) const
255 std::string name
, last_name
;
258 while( tar
.ReadNextFilenameOnly(name
) ) {
259 std::string::size_type pos
= name
.rfind('/');
260 if( pos
== std::string::npos
)
261 continue; // bad name
262 std::string dbname
= name
.substr(0, pos
);
264 if( dbname
!= last_name
) {
266 good
= restoreList
.IsSelected(dbname
);
274 /// Splits a tarpath of the form "DBName/DBID" into separate string values.
275 /// Returns true if successful, false if tarpath is a bad name.
276 bool DeviceInterface::SplitTarPath(const std::string
&tarpath
,
278 std::string
&dbid_text
,
280 uint32_t &dbid
) const
282 std::string::size_type pos
= tarpath
.rfind('/');
283 if( pos
== std::string::npos
)
284 return false; // bad name
286 dbname
= tarpath
.substr(0, pos
);
287 dbid_text
= tarpath
.substr(pos
+ 1);
288 if( dbname
.size() == 0 || dbid_text
.size() == 0 )
289 return false; // bad name
291 std::istringstream
iss(dbid_text
);
293 iss
>> std::hex
>> dbid
>> temp
;
294 dbrectype
= (uint8_t) temp
;
299 void DeviceInterface::SetThreadDBName(const std::string
&dbname
)
301 Glib::Mutex::Lock
lock(*m_dbnameMutex
);
302 m_current_dbname_not_thread_safe
= dbname
;
303 m_current_dbname
= dbname
;
306 //////////////////////////////////////////////////////////////////////////////
309 void DeviceInterface::Reset()
311 Usb::Device
dev(m_dev
->result
.m_dev
);
315 bool DeviceInterface::Connect()
319 m_con
= new Barry::Controller(m_dev
->result
);
320 m_desktop
= new Barry::Mode::Desktop(*m_con
);
324 catch( Barry::BadPassword
& ) {
325 // pass on to the caller
328 catch( Barry::BadSize
& ) {
329 // pass on to the caller
333 catch( Barry::Error
&e
) {
335 return False(e
.what());
339 bool DeviceInterface::Password(const char *password
)
342 m_desktop
->RetryPassword(password
);
345 catch( Barry::BadPassword
& ) {
346 // pass on to the caller
349 catch( Barry::Error
&e
) {
351 return False(e
.what());
355 void DeviceInterface::Disconnect()
364 // cycle through controller's DBDB and count the records in all the
365 // databases selected in the backupList
366 unsigned int DeviceInterface::GetRecordTotal(const Barry::ConfigFile::DBListType
&backupList
) const
368 unsigned int count
= 0;
370 Barry::DatabaseDatabase::DatabaseArrayType::const_iterator
371 i
= m_desktop
->GetDBDB().Databases
.begin();
372 for( ; i
!= m_desktop
->GetDBDB().Databases
.end(); ++i
) {
373 if( backupList
.IsSelected(i
->Name
) ) {
374 count
+= i
->RecordCount
;
380 unsigned int DeviceInterface::GetRecordTotal(const Barry::ConfigFile::DBListType
&restoreList
, const std::string
&filename
) const
382 unsigned int count
= 0;
384 std::auto_ptr
<reuse::TarFile
> tar
;
387 // do a scan through the tar file
388 tar
.reset( new reuse::TarFile(filename
.c_str(), false, &reuse::gztar_ops_nonthread
, true) );
389 count
= CountFiles(*tar
, restoreList
);
391 catch( reuse::TarFile::TarError
&te
) {
392 // just throw it away
397 /// returns name of database the thread is currently working on
398 std::string
DeviceInterface::GetThreadDBName() const
400 Glib::Mutex::Lock
lock(*m_dbnameMutex
);
401 return m_current_dbname_not_thread_safe
;
404 bool DeviceInterface::StartBackup(AppComm comm
,
405 const Barry::ConfigFile::DBListType
&backupList
,
406 const std::string
&directory
,
407 const std::string
&backupLabel
)
409 if( m_AppComm
.IsValid() )
410 return False("Thread already running.");
413 std::string filename
= directory
+ "/" + MakeFilename(backupLabel
);
414 m_tarback
.reset( new reuse::TarFile(filename
.c_str(), true, &reuse::gztar_ops_nonthread
, true) );
416 catch( reuse::TarFile::TarError
&te
) {
417 return False(te
.what());
422 m_dbList
= backupList
;
425 Glib::Thread::create(sigc::mem_fun(*this, &DeviceInterface::BackupThread
), false);
429 bool DeviceInterface::StartRestore(AppComm comm
,
430 const Barry::ConfigFile::DBListType
&restoreList
,
431 const std::string
&filename
)
433 if( m_AppComm
.IsValid() )
434 return False("Thread already running.");
437 // open for the main restore
438 m_tar
.reset( new reuse::TarFile(filename
.c_str(), false, &reuse::gztar_ops_nonthread
, true) );
441 catch( reuse::TarFile::TarError
&te
) {
442 return False(te
.what());
447 m_dbList
= restoreList
;
448 m_current_dbname_not_thread_safe
= "";
449 m_current_dbname
= "";
451 m_end_of_tar
= false;
452 m_tar_record_loaded
= false;
454 // get first tar record
458 Glib::Thread::create(sigc::mem_fun(*this, &DeviceInterface::RestoreThread
), false);
462 bool DeviceInterface::StartRestoreAndBackup(AppComm comm
,
463 const Barry::ConfigFile::DBListType
&restoreAndBackupList
,
464 const std::string
&filename
,
465 const std::string
&directory
)
467 if( m_AppComm
.IsValid() )
468 return False("Thread already running.");
471 // open for the main restore
472 m_tar
.reset( new reuse::TarFile(filename
.c_str(), false, &reuse::gztar_ops_nonthread
, true) );
474 // open for secondary backup
475 std::string back
= directory
+ "/" + MakeFilename(m_dev
->GetPIN().str());
476 m_tarback
.reset( new reuse::TarFile(back
.c_str(), true, &reuse::gztar_ops_nonthread
, true) );
479 catch( reuse::TarFile::TarError
&te
) {
480 return False(te
.what());
485 m_dbList
= restoreAndBackupList
;
486 m_current_dbname_not_thread_safe
= "";
487 m_current_dbname
= "";
489 m_end_of_tar
= false;
490 m_tar_record_loaded
= false;
492 // get first tar record
496 Glib::Thread::create(sigc::mem_fun(*this, &DeviceInterface::RestoreAndBackupThread
), false);
502 //////////////////////////////////////////////////////////////////////////////
503 // Barry::Parser overrides
505 void DeviceInterface::Clear()
509 void DeviceInterface::SetIds(uint8_t RecType
, uint32_t UniqueId
)
511 m_rec_type
= RecType
;
512 m_unique_id
= UniqueId
;
513 std::ostringstream oss
;
514 oss
<< std::hex
<< m_unique_id
<< " " << (unsigned int)m_rec_type
;
515 m_tar_id_text
= oss
.str();
516 if( m_tar_id_text
.size() == 0 )
517 throw std::runtime_error("No unique ID available!");
520 void DeviceInterface::ParseHeader(const Barry::Data
&data
, size_t &offset
)
524 void DeviceInterface::ParseFields(const Barry::Data
&data
,
526 const Barry::IConverter
*ic
)
528 m_record_data
.assign((const char*)data
.GetData() + offset
, data
.GetSize() - offset
);
531 void DeviceInterface::Store()
533 std::string tarname
= m_current_dbname
+ "/" + m_tar_id_text
;
534 m_tarback
->AppendFile(tarname
.c_str(), m_record_data
);
536 m_AppComm
.m_progress
->emit();
539 if( m_thread_quit
) {
545 //////////////////////////////////////////////////////////////////////////////
546 // Barry::Builder overrides
548 bool DeviceInterface::Retrieve(unsigned int dbId
)
553 // if loaded, we are likely on a database
554 // boundary, and the last read crossed it, so don't load again
555 if( m_tar_record_loaded
)
558 // search for a valid record
560 // load record data from tar file
561 std::string filename
;
562 if( !m_tar
->ReadNextFile(filename
, m_record_data
) ) {
563 // assume end of file
567 m_tar_record_loaded
= true;
569 // split record filename into dbname and ID
571 if( !SplitTarPath(filename
, dbname
, m_tar_id_text
, m_rec_type
, m_unique_id
) ) {
572 // invalid filename, skip it
573 std::cerr
<< "Skipping invalid tar record: " << filename
<< std::endl
;
577 // are we working on the same dbname as last time? if so, go ahead!
578 if( m_current_dbname
== dbname
) {
582 // DIFFERENT DBNAME from here on down!
584 // does the filter allow this record? if not, skip it and continue
586 if( !m_dbList
.IsSelected(dbname
) ) {
590 // all checks pass, load the new dbname, and return false
591 // if we are on a dbname boundary
593 if( m_current_dbname
.size() == 0 ) {
594 // this is the first time through Retrieve, so ok
598 SetThreadDBName(dbname
);
603 uint8_t DeviceInterface::GetRecType() const
608 uint32_t DeviceInterface::GetUniqueId() const
613 void DeviceInterface::BuildHeader(Barry::Data
&data
, size_t &offset
)
618 void DeviceInterface::BuildFields(Barry::Data
&data
, size_t &offset
,
619 const Barry::IConverter
*ic
)
621 int packet_size
= offset
+ m_record_data
.size();
622 unsigned char *buf
= data
.GetBuffer(packet_size
);
623 memcpy(buf
+ offset
, m_record_data
.data(), m_record_data
.size());
624 offset
+= m_record_data
.size();
625 data
.ReleaseBuffer(packet_size
);
627 // clear loaded flag, as it has now been used
628 m_tar_record_loaded
= false;
630 m_AppComm
.m_progress
->emit();
633 // helper function for halding restore errors
634 void DeviceInterface::SkipCurrentDB() throw()
636 // skip all records until next DB
638 while( Retrieve(0) ) {
639 std::cerr
<< "Skipping: "
640 << m_current_dbname
<< "/"
641 << m_tar_id_text
<< std::endl
;
642 m_tar_record_loaded
= false;
645 catch( reuse::TarFile::TarError
& ) {
649 // swallow all other exceptions
650 std::cerr
<< "EXCEPTION IN SkipCurrentDB()! "
651 "Please report to Barry mailing list." << std::endl
;
659 Device::Device(Barry::ProbeResult pr
) : result(pr
)