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"
32 DeviceInterface::DeviceInterface()
35 , m_dbnameMutex(new Glib::Mutex
) // this is just in an effort to
36 // avoid gtkmm headers in
39 , m_tar_record_loaded(false)
40 , m_thread_quit(false)
44 DeviceInterface::~DeviceInterface()
51 bool DeviceInterface::False(const std::string
&msg
)
57 void DeviceInterface::BackupThread()
60 m_thread_quit
= false;
61 m_last_thread_error
= "";
65 // cycle through all database names in the dbList
67 ConfigFile::DBListType::const_iterator name
= m_dbList
.begin();
68 for( ; name
!= m_dbList
.end(); ++name
) {
69 // save current db name
70 SetThreadDBName(*name
);
72 // call the controller to do the work
73 unsigned int dbId
= m_desktop
->GetDBID(*name
);
74 m_desktop
->LoadDatabase(dbId
, *this);
78 catch( Glib::Exception
&e
) {
79 m_last_thread_error
= e
.what();
82 catch( std::exception
&e
) {
83 m_last_thread_error
= e
.what();
87 m_last_thread_error
= "Terminated by user.";
94 m_AppComm
.m_error
->emit();
96 // signal host thread that we're done
97 m_AppComm
.m_done
->emit();
100 m_AppComm
.Invalidate();
103 void DeviceInterface::RestoreThread()
105 m_thread_quit
= false;
106 m_last_thread_error
= "";
110 // cycle until m_end_of_tar
111 while( !m_end_of_tar
) {
113 // call the controller to do the work
114 unsigned int dbId
= m_desktop
->GetDBID(m_current_dbname
);
115 m_AppComm
.m_erase_db
->emit();
116 m_desktop
->SaveDatabase(dbId
, *this);
118 catch( Barry::Error
&be
) {
120 m_last_thread_error
= "Error while restoring ";
121 m_last_thread_error
+= m_current_dbname
+ ". ";
122 m_last_thread_error
+= be
.what();
123 m_last_thread_error
+= " Will continue processing.";
125 // notify host thread
126 m_AppComm
.m_error
->emit();
128 // skip over records from this db
129 std::cerr
<< "Error on database: "
130 << m_current_dbname
<< std::endl
;
136 catch( Glib::Exception
&e
) {
137 m_last_thread_error
= e
.what();
138 m_AppComm
.m_error
->emit();
140 catch( std::exception
&e
) {
141 m_last_thread_error
= e
.what();
142 m_AppComm
.m_error
->emit();
145 m_last_thread_error
= "Terminated by user.";
151 // signal host thread that we're done
152 m_AppComm
.m_done
->emit();
155 m_AppComm
.Invalidate();
158 void DeviceInterface::RestoreAndBackupThread()
160 m_thread_quit
= false;
161 m_last_thread_error
= "";
165 // cycle until m_end_of_tar
166 while( !m_end_of_tar
) {
167 unsigned int dbId
= m_desktop
->GetDBID(m_current_dbname
);
172 m_AppComm
.m_erase_db
->emit();
173 m_desktop
->SaveDatabase(dbId
, *this);
176 catch( Barry::Error
&be
) {
178 m_last_thread_error
= "Error while restoring ";
179 m_last_thread_error
+= m_current_dbname
+ ". ";
180 m_last_thread_error
+= be
.what();
181 m_last_thread_error
+= " Will continue processing.";
183 // notify host thread
184 m_AppComm
.m_error
->emit();
186 // skip over records from this db
187 std::cerr
<< "Error on database: "
188 << m_current_dbname
<< std::endl
;
192 // then the backup, even if restore fails
193 m_desktop
->LoadDatabase(dbId
, *this);
198 catch( Glib::Exception
&e
) {
199 m_last_thread_error
= e
.what();
200 m_AppComm
.m_error
->emit();
202 catch( std::exception
&e
) {
203 m_last_thread_error
= e
.what();
204 m_AppComm
.m_error
->emit();
207 m_last_thread_error
= "Terminated by user.";
215 // signal host thread that we're done
216 m_AppComm
.m_done
->emit();
219 m_AppComm
.Invalidate();
222 std::string
DeviceInterface::MakeFilename(const std::string
&pin
)
224 time_t t
= time(NULL
);
225 struct tm
*lt
= localtime(&t
);
227 std::ostringstream tarfilename
;
228 tarfilename
<< pin
<< "-"
229 << std::setw(4) << std::setfill('0') << (lt
->tm_year
+ 1900)
230 << std::setw(2) << std::setfill('0') << (lt
->tm_mon
+ 1)
231 << std::setw(2) << std::setfill('0') << lt
->tm_mday
233 << std::setw(2) << std::setfill('0') << lt
->tm_hour
234 << std::setw(2) << std::setfill('0') << lt
->tm_min
235 << std::setw(2) << std::setfill('0') << lt
->tm_sec
237 return tarfilename
.str();
240 int DeviceInterface::CountFiles(reuse::TarFile
&tar
,
241 const ConfigFile::DBListType
&restoreList
)
244 std::string name
, last_name
;
247 while( tar
.ReadNextFilenameOnly(name
) ) {
248 std::string::size_type pos
= name
.rfind('/');
249 if( pos
== std::string::npos
)
250 continue; // bad name
251 std::string dbname
= name
.substr(0, pos
);
253 if( dbname
!= last_name
) {
255 good
= restoreList
.IsSelected(dbname
);
263 /// Splits a tarpath of the form "DBName/DBID" into separate string values.
264 /// Returns true if successful, false if tarpath is a bad name.
265 bool DeviceInterface::SplitTarPath(const std::string
&tarpath
,
267 std::string
&dbid_text
,
271 std::string::size_type pos
= tarpath
.rfind('/');
272 if( pos
== std::string::npos
)
273 return false; // bad name
275 dbname
= tarpath
.substr(0, pos
);
276 dbid_text
= tarpath
.substr(pos
+ 1);
277 if( dbname
.size() == 0 || dbid_text
.size() == 0 )
278 return false; // bad name
280 std::istringstream
iss(dbid_text
);
282 iss
>> std::hex
>> dbid
>> temp
;
283 dbrectype
= (uint8_t) temp
;
288 void DeviceInterface::SetThreadDBName(const std::string
&dbname
)
290 Glib::Mutex::Lock
lock(*m_dbnameMutex
);
291 m_current_dbname_not_thread_safe
= dbname
;
292 m_current_dbname
= dbname
;
296 //////////////////////////////////////////////////////////////////////////////
299 bool DeviceInterface::Connect(const Barry::ProbeResult
&dev
)
303 m_con
= new Barry::Controller(dev
);
304 m_desktop
= new Barry::Mode::Desktop(*m_con
);
308 catch( Barry::BadPassword
& ) {
309 // pass on to the caller
312 catch( Barry::BadSize
& ) {
313 // pass on to the caller
317 catch( Barry::Error
&e
) {
319 return False(e
.what());
323 bool DeviceInterface::Password(const char *password
)
326 m_desktop
->RetryPassword(password
);
329 catch( Barry::BadPassword
& ) {
330 // pass on to the caller
333 catch( Barry::Error
&e
) {
335 return False(e
.what());
339 void DeviceInterface::Disconnect()
348 // cycle through controller's DBDB and count the records in all the
349 // databases selected in the backupList
350 int DeviceInterface::GetDeviceRecordTotal(const ConfigFile::DBListType
&backupList
) const
354 Barry::DatabaseDatabase::DatabaseArrayType::const_iterator
355 i
= m_desktop
->GetDBDB().Databases
.begin();
356 for( ; i
!= m_desktop
->GetDBDB().Databases
.end(); ++i
) {
357 if( backupList
.IsSelected(i
->Name
) ) {
358 count
+= i
->RecordCount
;
364 /// returns name of database the thread is currently working on
365 std::string
DeviceInterface::GetThreadDBName() const
367 Glib::Mutex::Lock
lock(*m_dbnameMutex
);
368 return m_current_dbname_not_thread_safe
;
371 bool DeviceInterface::StartBackup(AppComm comm
,
372 const ConfigFile::DBListType
&backupList
,
373 const std::string
&directory
,
374 const std::string
&pin
)
376 if( m_AppComm
.IsValid() )
377 return False("Thread already running.");
381 std::string filename
= directory
+ "/" + MakeFilename(pin
);
382 m_tarback
.reset( new reuse::TarFile(filename
.c_str(), true, &reuse::gztar_ops_nonthread
, true) );
385 catch( reuse::TarFile::TarError
&te
) {
386 return False(te
.what());
391 m_dbList
= backupList
;
394 Glib::Thread::create(sigc::mem_fun(*this, &DeviceInterface::BackupThread
), false);
398 bool DeviceInterface::StartRestore(AppComm comm
,
399 const ConfigFile::DBListType
&restoreList
,
400 const std::string
&filename
,
403 if( m_AppComm
.IsValid() )
404 return False("Thread already running.");
408 // caller is asking for a total, so we do a quick
409 // scan through the tar file first
410 m_tar
.reset( new reuse::TarFile(filename
.c_str(), false, &reuse::gztar_ops_nonthread
, true) );
411 *pRecordCount
= CountFiles(*m_tar
, restoreList
);
413 // close for next open
417 // open for the main restore
418 m_tar
.reset( new reuse::TarFile(filename
.c_str(), false, &reuse::gztar_ops_nonthread
, true) );
421 catch( reuse::TarFile::TarError
&te
) {
422 return False(te
.what());
427 m_dbList
= restoreList
;
428 m_current_dbname_not_thread_safe
= "";
429 m_current_dbname
= "";
431 m_end_of_tar
= false;
432 m_tar_record_loaded
= false;
434 // get first tar record
438 Glib::Thread::create(sigc::mem_fun(*this, &DeviceInterface::RestoreThread
), false);
442 bool DeviceInterface::StartRestoreAndBackup(AppComm comm
,
443 const ConfigFile::DBListType
&restoreAndBackupList
,
444 const std::string
&filename
,
445 const std::string
&directory
, const std::string
&pin
,
448 if( m_AppComm
.IsValid() )
449 return False("Thread already running.");
453 // caller is asking for a total, so we do a quick
454 // scan through the tar file first
455 m_tar
.reset( new reuse::TarFile(filename
.c_str(), false, &reuse::gztar_ops_nonthread
, true) );
456 *pRecordCount
= CountFiles(*m_tar
, restoreAndBackupList
);
458 // close for next open
462 // open for the main restore
463 m_tar
.reset( new reuse::TarFile(filename
.c_str(), false, &reuse::gztar_ops_nonthread
, true) );
465 // open for secondary backup
466 std::string back
= directory
+ "/" + MakeFilename(pin
);
467 m_tarback
.reset( new reuse::TarFile(back
.c_str(), true, &reuse::gztar_ops_nonthread
, true) );
470 catch( reuse::TarFile::TarError
&te
) {
471 return False(te
.what());
476 m_dbList
= restoreAndBackupList
;
477 m_current_dbname_not_thread_safe
= "";
478 m_current_dbname
= "";
480 m_end_of_tar
= false;
481 m_tar_record_loaded
= false;
483 // get first tar record
487 Glib::Thread::create(sigc::mem_fun(*this, &DeviceInterface::RestoreAndBackupThread
), false);
493 //////////////////////////////////////////////////////////////////////////////
494 // Barry::Parser overrides
496 void DeviceInterface::SetIds(uint8_t RecType
, uint32_t UniqueId
)
498 m_rec_type
= RecType
;
499 m_unique_id
= UniqueId
;
500 std::ostringstream oss
;
501 oss
<< std::hex
<< m_unique_id
<< " " << (unsigned int)m_rec_type
;
502 m_tar_id_text
= oss
.str();
503 if( m_tar_id_text
.size() == 0 )
504 throw std::runtime_error("No unique ID available!");
507 void DeviceInterface::ParseFields(const Barry::Data
&data
, size_t &offset
)
509 m_record_data
.assign((const char*)data
.GetData() + offset
, data
.GetSize() - offset
);
512 void DeviceInterface::Store()
514 std::string tarname
= m_current_dbname
+ "/" + m_tar_id_text
;
515 m_tarback
->AppendFile(tarname
.c_str(), m_record_data
);
517 m_AppComm
.m_progress
->emit();
520 if( m_thread_quit
) {
526 //////////////////////////////////////////////////////////////////////////////
527 // Barry::Builder overrides
529 bool DeviceInterface::Retrieve(unsigned int dbId
)
534 // if loaded, we are likely on a database
535 // boundary, and the last read crossed it, so don't load again
536 if( m_tar_record_loaded
)
539 // search for a valid record
541 // load record data from tar file
542 std::string filename
;
543 if( !m_tar
->ReadNextFile(filename
, m_record_data
) ) {
544 // assume end of file
548 m_tar_record_loaded
= true;
550 // split record filename into dbname and ID
552 if( !SplitTarPath(filename
, dbname
, m_tar_id_text
, m_rec_type
, m_unique_id
) ) {
553 // invalid filename, skip it
554 std::cerr
<< "Skipping invalid tar record: " << filename
<< std::endl
;
558 // are we working on the same dbname as last time? if so, go ahead!
559 if( m_current_dbname
== dbname
) {
563 // DIFFERENT DBNAME from here on down!
565 // does the filter allow this record? if not, skip it and continue
567 if( !m_dbList
.IsSelected(dbname
) ) {
571 // all checks pass, load the new dbname, and return false
572 // if we are on a dbname boundary
574 if( m_current_dbname
.size() == 0 ) {
575 // this is the first time through Retrieve, so ok
579 SetThreadDBName(dbname
);
584 uint8_t DeviceInterface::GetRecType() const
589 uint32_t DeviceInterface::GetUniqueId() const
594 void DeviceInterface::BuildHeader(Barry::Data
&data
, size_t &offset
)
599 void DeviceInterface::BuildFields(Barry::Data
&data
, size_t &offset
)
601 int packet_size
= offset
+ m_record_data
.size();
602 unsigned char *buf
= data
.GetBuffer(packet_size
);
603 memcpy(buf
+ offset
, m_record_data
.data(), m_record_data
.size());
604 offset
+= m_record_data
.size();
605 data
.ReleaseBuffer(packet_size
);
607 // clear loaded flag, as it has now been used
608 m_tar_record_loaded
= false;
610 m_AppComm
.m_progress
->emit();
613 // helper function for halding restore errors
614 void DeviceInterface::SkipCurrentDB() throw()
616 // skip all records until next DB
618 while( Retrieve(0) ) {
619 std::cerr
<< "Skipping: "
620 << m_current_dbname
<< "/"
621 << m_tar_id_text
<< std::endl
;
622 m_tar_record_loaded
= false;
625 catch( reuse::TarFile::TarError
& ) {
629 // swallow all other exceptions
630 std::cerr
<< "EXCEPTION IN SkipCurrentDB()! "
631 "Please report to Barry mailing list." << std::endl
;