os4x: run plugin as process
[barry.git] / gui / src / DeviceIface.cc
blob6b9cbcb55df6da7a94ebef272ff59343200950ba
1 ///
2 /// \file DeviceIface.cc
3 /// Interface class for device backup and restore
4 ///
6 /*
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"
23 #include "util.h"
24 #include <glibmm.h>
25 #include <iostream>
26 #include <iomanip>
27 #include <sstream>
28 #include <time.h>
29 #include <string.h>
30 #include <stdlib.h>
32 DeviceInterface::DeviceInterface(Device *dev)
33 : m_dev(dev)
34 , m_con(0)
35 , m_desktop(0)
36 , m_dbnameMutex(new Glib::Mutex) // this is just in an effort to
37 // avoid gtkmm headers in
38 // DeviceIface.h
39 , m_end_of_tar(false)
40 , m_tar_record_loaded(false)
41 , m_thread_quit(false)
45 DeviceInterface::~DeviceInterface()
47 Disconnect();
48 delete m_dbnameMutex;
51 bool DeviceInterface::False(const std::string &msg)
53 m_last_error = msg;
54 return false;
57 void DeviceInterface::BackupThread()
59 bool error = false;
60 m_thread_quit = false;
61 m_last_thread_error = "";
63 try {
64 // cycle through all database names in the dbList
65 // and store them all
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();
77 error = true;
79 catch( std::exception &e ) {
80 m_last_thread_error = e.what();
81 error = true;
83 catch( Quit &q ) {
84 m_last_thread_error = "Terminated by user.";
87 m_tarback->Close();
88 m_tarback.reset();
90 if( error )
91 m_AppComm.m_error->emit();
93 // signal host thread that we're done
94 m_AppComm.m_done->emit();
96 // done!
97 m_AppComm.Invalidate();
100 void DeviceInterface::RestoreThread()
102 m_thread_quit = false;
103 m_last_thread_error = "";
105 try {
107 // cycle until m_end_of_tar
108 while( !m_end_of_tar ) {
109 try {
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 ) {
117 // save thread error
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;
129 SkipCurrentDB();
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();
142 catch( Quit &q ) {
143 m_last_thread_error = "Terminated by user.";
146 m_tar->Close();
147 m_tar.reset();
149 // signal host thread that we're done
150 m_AppComm.m_done->emit();
152 // done!
153 m_AppComm.Invalidate();
156 void DeviceInterface::RestoreAndBackupThread()
158 m_thread_quit = false;
159 m_last_thread_error = "";
161 try {
163 // cycle until m_end_of_tar
164 while( !m_end_of_tar ) {
165 unsigned int dbId = m_desktop->GetDBID(m_current_dbname);
167 try {
169 // do restore first
170 m_AppComm.m_erase_db->emit();
171 m_desktop->SaveDatabase(dbId, *this);
174 catch( Barry::Error &be ) {
175 // save thread error
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;
187 SkipCurrentDB();
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();
204 catch( Quit &q ) {
205 m_last_thread_error = "Terminated by user.";
208 m_tar->Close();
209 m_tar.reset();
210 m_tarback->Close();
211 m_tarback.reset();
213 // signal host thread that we're done
214 m_AppComm.m_done->emit();
216 // done!
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() ) {
227 // prepend a hyphen
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] == ' ' )
233 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
242 << "-"
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
246 << fileLabel
247 << ".tar.gz";
248 return tarfilename.str();
251 int DeviceInterface::CountFiles(reuse::TarFile &tar,
252 const Barry::ConfigFile::DBListType &restoreList) const
254 int count = 0;
255 std::string name, last_name;
256 bool good = false;
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 ) {
265 last_name = dbname;
266 good = restoreList.IsSelected(dbname);
268 if( good )
269 count++;
271 return count;
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,
277 std::string &dbname,
278 std::string &dbid_text,
279 uint8_t &dbrectype,
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);
292 unsigned int temp;
293 iss >> std::hex >> dbid >> temp;
294 dbrectype = (uint8_t) temp;
296 return true;
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 //////////////////////////////////////////////////////////////////////////////
307 // Public API
309 void DeviceInterface::Reset()
311 Usb::Device dev(m_dev->result.m_dev);
312 dev.Reset();
315 bool DeviceInterface::Connect()
317 try {
318 Disconnect();
319 m_con = new Barry::Controller(m_dev->result);
320 m_desktop = new Barry::Mode::Desktop(*m_con);
321 m_desktop->Open();
322 return true;
324 catch( Barry::BadPassword & ) {
325 // pass on to the caller
326 throw;
328 catch( Barry::BadSize & ) {
329 // pass on to the caller
330 Disconnect();
331 throw;
333 catch( Barry::Error &e ) {
334 Disconnect();
335 return False(e.what());
339 bool DeviceInterface::Password(const char *password)
341 try {
342 m_desktop->RetryPassword(password);
343 return true;
345 catch( Barry::BadPassword & ) {
346 // pass on to the caller
347 throw;
349 catch( Barry::Error &e ) {
350 Disconnect();
351 return False(e.what());
355 void DeviceInterface::Disconnect()
357 delete m_desktop;
358 m_desktop = 0;
360 delete m_con;
361 m_con = 0;
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;
377 return count;
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;
386 try {
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
394 return count;
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.");
412 try {
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());
420 // setup
421 m_AppComm = comm;
422 m_dbList = backupList;
424 // start the thread
425 Glib::Thread::create(sigc::mem_fun(*this, &DeviceInterface::BackupThread), false);
426 return true;
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.");
436 try {
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());
445 // setup
446 m_AppComm = comm;
447 m_dbList = restoreList;
448 m_current_dbname_not_thread_safe = "";
449 m_current_dbname = "";
450 m_unique_id = 0;
451 m_end_of_tar = false;
452 m_tar_record_loaded = false;
454 // get first tar record
455 Retrieve(0);
457 // start the thread
458 Glib::Thread::create(sigc::mem_fun(*this, &DeviceInterface::RestoreThread), false);
459 return true;
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.");
470 try {
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());
483 // setup
484 m_AppComm = comm;
485 m_dbList = restoreAndBackupList;
486 m_current_dbname_not_thread_safe = "";
487 m_current_dbname = "";
488 m_unique_id = 0;
489 m_end_of_tar = false;
490 m_tar_record_loaded = false;
492 // get first tar record
493 Retrieve(0);
495 // start the thread
496 Glib::Thread::create(sigc::mem_fun(*this, &DeviceInterface::RestoreAndBackupThread), false);
497 return true;
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,
525 size_t &offset,
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();
538 // check quit flag
539 if( m_thread_quit ) {
540 throw Quit();
545 //////////////////////////////////////////////////////////////////////////////
546 // Barry::Builder overrides
548 bool DeviceInterface::Retrieve(unsigned int dbId)
550 if( m_end_of_tar )
551 return false;
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 )
556 return true;
558 // search for a valid record
559 for(;;) {
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
564 m_end_of_tar = true;
565 return false;
567 m_tar_record_loaded = true;
569 // split record filename into dbname and ID
570 std::string dbname;
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;
574 continue;
577 // are we working on the same dbname as last time? if so, go ahead!
578 if( m_current_dbname == dbname ) {
579 return true;
582 // DIFFERENT DBNAME from here on down!
584 // does the filter allow this record? if not, skip it and continue
585 // looking
586 if( !m_dbList.IsSelected(dbname) ) {
587 continue;
590 // all checks pass, load the new dbname, and return false
591 // if we are on a dbname boundary
592 bool r_val = false;
593 if( m_current_dbname.size() == 0 ) {
594 // this is the first time through Retrieve, so ok
595 r_val = true;
598 SetThreadDBName(dbname);
599 return r_val;
603 uint8_t DeviceInterface::GetRecType() const
605 return m_rec_type;
608 uint32_t DeviceInterface::GetUniqueId() const
610 return m_unique_id;
613 void DeviceInterface::BuildHeader(Barry::Data &data, size_t &offset)
615 // nothing to do
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
637 try {
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 & ) {
646 m_end_of_tar = true;
648 catch( ... ) {
649 // swallow all other exceptions
650 std::cerr << "EXCEPTION IN SkipCurrentDB()! "
651 "Please report to Barry mailing list." << std::endl;
655 Device::Device()
659 Device::Device(Barry::ProbeResult pr) : result(pr)