/*************************************************************************** xmondorestore.cpp - restore functions ------------------------------------- begin : Sun Nov 22 2003 copyright : (C) 2003 by Joshua Oreman email : oremanj@get-linux.org cvsid : $Id: xmondorestore.cpp 273 2006-01-03 15:09:12Z bcornec $ ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include "xmondo.h" #include "xmondorestore.h" #include extern "C" { #define bool int #include #include #include #include #include #undef bool int restore_everything (struct s_bkpinfo *bkpinfo, struct s_node *filelist); int get_cfg_file_from_archive (struct s_bkpinfo *bkpinfo); int read_cfg_file_into_bkpinfo (char *cfg_file, struct s_bkpinfo *bkpinfo); int mount_cdrom (struct s_bkpinfo *bkpinfo); void setup_MR_global_filenames (struct s_bkpinfo *bkpinfo); extern char *g_mondo_cfg_file; extern char *g_filelist_full; extern int g_current_media_number; extern int g_text_mode; } extern QProgressBar *XMondoProgress; extern QLabel *XMondoProgressWhat, *XMondoProgressWhat2, *XMondoProgressWhat3; extern QCheckBox *XMondoVerbose; extern QMultiLineEdit *XMondoLog; extern QLabel *XMondoStatus; extern QLabel *XMondoTimeTaken, *XMondoTimeToGo, *XMondoProgressPercent; extern QPushButton *XMondoCancel; extern XMEventHolder events; class XMCheckListItem : public QCheckListItem { public: XMCheckListItem (QCheckListItem * parent, const QString & text, Type tt = CheckBox) : QCheckListItem (parent, text, tt), _text (text) { } XMCheckListItem (QListViewItem * parent, const QString & text, Type tt = CheckBox) : QCheckListItem (parent, text, tt), _text (text) { } XMCheckListItem (QListView * parent, const QString & text, Type tt = CheckBox) : QCheckListItem (parent, text, tt), _text (text) { } virtual ~XMCheckListItem() {} virtual int rtti() { return 1001; } virtual void setOn (bool on) { QCheckListItem::setOn (on); } static QLabel *progressDisplay; static bool *doneSetup; static bool selecting; static int counter; static int depth; protected: virtual void stateChange (bool on) { if (firstChild()) { if (!selecting) { progressDisplay->setText (QString ("%1electing %2...").arg (on? "S" : "Des").arg (_text)); counter = depth = 0; selecting = true; if (doneSetup) *doneSetup = false; } for (QListViewItem *lvi = firstChild(); lvi; lvi = lvi->nextSibling()) { XMCheckListItem *cli; if ((cli = dynamic_cast (lvi)) != 0) { if (!(++counter % 10000)) { progressDisplay->setText (progressDisplay->text() + "."); } if (!(counter % 1000)) { kapp->processEvents(); } depth++; cli->setOn (on); depth--; } } if (depth == 0) { selecting = false; progressDisplay->setText ("Please select files and directories to be restored."); if (doneSetup) *doneSetup = true; } } } private: // Ugly hack, but it works: fix segfault QString _text; }; QLabel *XMCheckListItem::progressDisplay = 0; bool XMCheckListItem::selecting = false; bool *XMCheckListItem::doneSetup = 0; int XMCheckListItem::counter = 0; int XMCheckListItem::depth = 0; struct XM_node { QString s; XM_node *firstChild; XM_node *nextSibling; }; #define XMLF_OK 0 #define XMLF_CANTOPEN 1 // Requirements: // - File must be sorted // - Entry for a directory must come directly before entries for its files void XM_load_filelist_sub (FILE *fp, QStringList first, XMCheckListItem *top, QLabel *status, int numlines) { static int depth = 0; static int lino = 0; bool tristated = false; char line[4095]; while (fgets (line, 4095, fp)) { bool chomped = false; if (line[strlen (line) - 1] == '\n') { line[strlen (line) - 1] = 0; chomped = true; } QStringList dirComponents = QStringList::split ("/", line); if (dirComponents.size() == 0) continue; if (depth > 0) { bool good = true; for (int i = 0; (i < dirComponents.size()) && (i < first.size()) && (i < depth); ++i) if (dirComponents[i] != first[i]) good = false; if (!good) { fseek (fp, -(strlen (line) + chomped), SEEK_CUR); // equivalent of "pushback" return; } } XMCheckListItem *newitem = new XMCheckListItem (top, dirComponents[dirComponents.size() - 1]); if (!(++lino % 1111)) { status->setText (QString ("Loading filelist - %1% done").arg (lino * 100 / numlines)); } depth++; XM_load_filelist_sub (fp, dirComponents, newitem, status, numlines); depth--; } } int XM_load_filelist (const char *filelist_fname, QListView *list, QLabel *status, QStringList first = QStringList(), FILE *fp = 0) { fp = fopen (filelist_fname, "r"); if (!fp) return XMLF_CANTOPEN; XMCheckListItem *top = new XMCheckListItem (list, "/"); status->setText ("Loading filelist - 0% done"); XM_load_filelist_sub (fp, QStringList(), top, status, atoi (call_program_and_get_last_line_of_output (const_cast (QString ("wc -l %1").arg (filelist_fname).ascii())))); fclose (fp); return XMLF_OK; } void *XMondoRestore_preparer_thread (void *arg) { XMondoRestore *r = static_cast (arg); int i = 0; g_current_media_number = 1; r->fStatusMsg->setText ("Retrieving mondo-restore.cfg from archive..."); struct s_bkpinfo *bkpinfo = r->bkpinfo; reset_bkpinfo (bkpinfo); switch (r->rMediaType->id (r->rMediaType->selected())) { case 0: bkpinfo->backup_media_type = cdr; break; case 1: bkpinfo->backup_media_type = cdrw; bkpinfo->wipe_media_first = 1; break; case 2: bkpinfo->backup_media_type = cdstream; break; case 3: bkpinfo->backup_media_type = dvd; break; case 4: bkpinfo->backup_media_type = iso; break; case 5: bkpinfo->backup_media_type = nfs; break; case 6: bkpinfo->backup_media_type = tape; break; case 7: bkpinfo->backup_media_type = udev; break; } strcpy (bkpinfo->media_device, r->rDevice->text()); if (bkpinfo->backup_media_type == nfs) strcpy (bkpinfo->nfs_mount, r->rDevice->text()); if (bkpinfo->backup_media_type == nfs) strcpy (bkpinfo->nfs_remote_dir, r->rNFSRemoteDir->text()); if (bkpinfo->backup_media_type == iso) strcpy (bkpinfo->isodir, r->rDevice->text()); strcpy (bkpinfo->tmpdir, r->tempdir.ascii()); setup_MR_global_filenames (bkpinfo); if (get_cfg_file_from_archive (bkpinfo) != 0) { r->fStatusMsg->setText ("Unable to retrieve mondo-restore.cfg. Please choose another archive source."); r->ok = false; return 0; } read_cfg_file_into_bkpinfo (g_mondo_cfg_file, bkpinfo); if (!does_file_exist (g_mondo_cfg_file)) { r->fStatusMsg->setText ("Unable to retrieve mondo-restore.cfg. Internal error."); r->ok = false; return 0; } if (!does_file_exist (g_filelist_full)) { chdir (bkpinfo->tmpdir); r->fStatusMsg->setText ("Retrieving filelist..."); if ((bkpinfo->backup_media_type == tape) || (bkpinfo->backup_media_type == udev) || (bkpinfo->backup_media_type == cdstream)) { unlink (g_filelist_full); system (QString ("tar -zxf %1 tmp/filelist.full").arg (bkpinfo->media_device).ascii()); } else { mount_cdrom (bkpinfo); unlink (g_filelist_full); system ("tar -zxf /mnt/cdrom/images/all.tar.gz tmp/filelist.full"); } if (!does_file_exist (g_filelist_full)) { r->fStatusMsg->setText ("Filelist could not be retrieved!"); r->ok = false; return 0; } } if (!r->rFilter->text().isEmpty() && !r->rFilter->text().isNull()) { r->fStatusMsg->setText (QString ("Filtering filelist through regexp %1...").arg (r->rFilter->text())); if (system (QString ("egrep '%2' %1 > %3.FILT").arg (g_filelist_full).arg (r->rFilter->text()).arg (g_filelist_full).ascii()) != 0) { r->fStatusMsg->setText ("Filter failed, using whole filelist"); rename (g_filelist_full, QString ("%1.FILT").arg (g_filelist_full).ascii()); usleep (500000); } } else { rename (g_filelist_full, QString ("%1.FILT").arg (g_filelist_full).ascii()); } r->fStatusMsg->setText ("Preparing filelist - 0% done"); int nlines = atoi (call_program_and_get_last_line_of_output (const_cast (QString ("wc -l %1.FILT").arg (g_filelist_full).ascii()))); int curline = 0; FILE *fin = fopen (QString ("%1.FILT").arg (g_filelist_full).ascii(), "r"); FILE *fout = popen (QString ("sort | uniq > %1").arg (g_filelist_full).ascii(), "w"); if (!(fin && fout)) { r->fStatusMsg->setText ("Can't open filelist"); r->ok = false; return 0; } char line[4096], tmp[4096]; while (fgets (line, 4096, fin)) { if (line[strlen (line) - 1] == '\n') line[strlen (line) - 1] = '\0'; for (int pos = 0; line[pos] != '\0'; pos++) { if (line[pos] != '/') continue; strcpy (tmp, line); tmp[pos] = '\0'; if (strlen (tmp)) { fprintf (fout, "%s\n", tmp); } } fprintf (fout, "%s\n", line); if (!(++curline % 1111)) { r->fStatusMsg->setText (QString ("Preparing filelist - %1% done").arg (curline * 100 / nlines)); } } fclose (fin); pclose (fout); if (XM_load_filelist (g_filelist_full, r->fList, r->fStatusMsg) != XMLF_OK) { r->fStatusMsg->setText ("Error loading filelist"); r->ok = false; return 0; } r->fStatusMsg->setText ("Filelist loaded OK"); r->doneSetup = true; r->fList->setEnabled (true); r->fRestoreDirLabel->setEnabled (true); r->fRestoreDir->setEnabled (true); sleep (1); r->fStatusMsg->setText ("Please select files and directories to be restored."); XMCheckListItem::progressDisplay = r->fStatusMsg; XMCheckListItem::doneSetup = &(r->doneSetup); return 0; } XMondoRestore::XMondoRestore (QWidget *parent, QButtonGroup *mediaType, QLineEdit *device, QLineEdit *nfsRemoteDir, QLineEdit *filelistFilter) : QObject (0, 0), rMediaType (mediaType), rDevice (device), rNFSRemoteDir (nfsRemoteDir), rFilter (filelistFilter), ok (true), files (parent), doneSetup (false), th (0) { bkpinfo = new s_bkpinfo; char tmp[256]; strcpy (tmp, "/tmp/xmondo.rstr.XXXXXX"); mktemp (tmp); tempdir = tmp; filelistLocation = tempdir + "/filelist.full"; cfgLocation = tempdir + "/mondo-restore.cfg"; cdMountpoint = "/mnt/cdrom"; if (!does_file_exist ("/mnt/cdrom")) { if (system ("mkdir -p /mnt/cdrom >/dev/null 2>&1") != 0) { popup_and_OK ("Can't create /mnt/cdrom directory. Aborting restore."); ok = false; return; } } QGridLayout *filesGrid; filesGrid = new QGridLayout (files, 3, 2, 5, 5, "filesGrid"); fStatusMsg = new QLabel ("", files); fList = new QListView (files); fRestoreDirLabel = new QLabel ("Restore to:", files); fRestoreDir = new QLineEdit (files); fList->addColumn ("Files to restore:"); fList->setRootIsDecorated (true); fList->setEnabled (false); fRestoreDirLabel->setEnabled (false); fRestoreDir->setEnabled (false); fRestoreDir->setText ("/tmp"); filesGrid->addMultiCellWidget (fStatusMsg, 0, 0, 0, 1); filesGrid->addMultiCellWidget (fList, 1, 1, 0, 1); filesGrid->addWidget (fRestoreDirLabel, 2, 0); filesGrid->addWidget (fRestoreDir, 2, 1); while (system ("mount | grep -q /mnt/cdrom") == 1) { if (QMessageBox::warning (0, "XMondo", QString ("CD is mounted. Please unmount it and try again."), "&Retry", "&Cancel") == 1 /* Cancel */) { ok = false; return; } } pthread_create (&preparer_thread, 0, XMondoRestore_preparer_thread, static_cast (this)); ok = true; } XMondoRestore::~XMondoRestore() { chdir ("/"); pthread_cancel (preparer_thread); system (QString ("mount | grep xmondo.rstr | cut -d' ' -f3 | xargs umount")); system (QString ("umount %1/mount.bootdisk >/dev/null 2>&1").arg (tempdir).ascii()); if ((tempdir != "") && (tempdir != "/") && (tempdir != "/tmp") && (tempdir != "/tmp/")) { system (QString ("rm -rf %1").arg (tempdir).ascii()); } system (QString ("umount %1 >/dev/null 2>&1").arg (cdMountpoint).ascii()); delete bkpinfo; } int XM_save_filelist (QListViewItem *liTop, const char *fname, QString prefix = QString (""), int numlines = 0, int curline = 0, FILE *fp = 0) { static int depth = 0; if (!depth) { numlines = atoi (call_program_and_get_last_line_of_output (const_cast (QString ("wc -l %1").arg (fname).ascii()))); fp = fopen (fname, "w"); if (!fp) return 1; fprintf (fp, "/\n"); if (!liTop) { popup_and_OK ("Internal error. No entries in filelist."); return 255; } } XMCheckListItem *top = dynamic_cast (liTop); if (!top) return 2; for (; top; top = dynamic_cast (top->nextSibling())) { if (!top) return 2; if (top->isOn()) { fprintf (fp, "%s/%s\n", prefix.ascii(), top->text().ascii()); } if (!(++curline % 1000)) { XMondoProgress->setProgress (curline * 100 / numlines); kapp->processEvents(); } if (top->firstChild()) { depth++; XM_save_filelist (top->firstChild(), "", prefix + "/" + top->text(), numlines, curline, fp); depth--; } } if (!depth) { fclose (fp); } return 0; } void *RestoreThread__run (void *arg); class RestoreThread { friend void *RestoreThread__run (void *arg); public: RestoreThread (struct s_bkpinfo *bkpinfo, struct s_node *filelist) : _bkpinfo (bkpinfo), _filelist (filelist), _ret (-1), _aborted (false) {} int returns() { return _ret; } void start() { _running = true; pthread_create (&_thr, 0, RestoreThread__run, this); } void terminate() { _running = false; pthread_cancel (_thr); _thr = 0; } bool running() { return _running; } bool aborted() { return _aborted; } protected: static void setStop (void *arg) { RestoreThread *rt = static_cast (arg); if (!rt) return; rt->_running = false; rt->_ret = -1; rt->_aborted = true; } void run() { pthread_cleanup_push (RestoreThread::setStop, this); log_to_screen ("Restore started on %s", call_program_and_get_last_line_of_output ("date")); _ret = restore_everything (_bkpinfo, _filelist); log_to_screen ("Restore finished on %s", call_program_and_get_last_line_of_output ("date")); pthread_cleanup_pop (FALSE); _running = false; } struct s_bkpinfo *_bkpinfo; struct s_node *_filelist; int _ret; pthread_t _thr; bool _running; bool _aborted; }; void *RestoreThread__run (void *arg) { RestoreThread *rt = static_cast (arg); if (!rt && (sizeof(void*) >= 4)) return (void *) 0xDEADBEEF; rt->run(); } void XMondoRestore::slotAbortRestore() { if (th) th->terminate(); } void XM_toggle_everything_on (struct s_node *flist) { for (; flist; flist = flist->right) { if (flist->ch == 0) { flist->selected = 1; } if (flist->down) { XM_toggle_everything_on (flist->down); } } } void XMondoRestore::go() { if (!doneSetup) { popup_and_OK ("Please wait for the setup to be completed."); ok = false; return; } pthread_cancel (preparer_thread); disconnect (XMondoCancel, SIGNAL(clicked()), 0, 0); connect (XMondoCancel, SIGNAL(clicked()), this, SLOT(slotAbortRestore())); XMondoProgress->show(); XMondoProgress->setTotalSteps (100); XMondoProgress->setProgress (0); XMondoProgressWhat->show(); XMondoProgressWhat->setText ("Saving your filelist choices."); XMondoProgressWhat2->hide(); XMondoProgressWhat3->hide(); XMondoTimeTaken->hide(); XMondoTimeToGo->hide(); XMondoStatus->setText ("Saving filelist"); XMondoStatus->show(); XMondoLog->setText (""); XMondoLog->show(); if (XM_save_filelist (fList->firstChild()->firstChild(), g_filelist_full) != 0) { XMondoStatus->setText ("Error saving filelist"); ok = false; return; } XMondoStatus->setText ("Reloading filelist"); /*DEBUG*/ system (QString ("cp %1 /home/oremanj/filelist.tested").arg (g_filelist_full).ascii()); s_node *flist = load_filelist (g_filelist_full); if (!flist) { XMondoStatus->setText ("Error loading filelist"); ok = false; return; } XM_toggle_everything_on (flist); XMondoProgress->hide(); XMondoStatus->setText ("Beginning restore"); XMondoProgressWhat->hide(); strcpy (bkpinfo->restore_path, fRestoreDir->text().ascii()); g_text_mode = 1; // avoid crashing on NEWT functions th = new RestoreThread (bkpinfo, flist); th->start(); while (th->running()) { usleep (100000); events.send(); kapp->processEvents(); } free_filelist (flist); if (th->aborted()) { /* do nothing */ } else if (th->returns() == 0) { popup_and_OK ("Restore completed with no errors."); } else { popup_and_OK ("Restore completed; however, there were some errors."); } delete th; th = 0; ok = true; // call destructor in caller }