source: src-qt4/pc-updategui/mainWin.cpp @ 53791e4

9.2-releasereleng/10.0releng/10.0.1releng/10.0.2releng/10.0.3releng/10.1
Last change on this file since 53791e4 was 53791e4, checked in by Kris Moore <kris@…>, 15 months ago

Add a better system of locking when GUIs can run freebsd-update, this will prevent
some messed up situations where a current freebsd-update is being screwed up by another
running process.

  • Property mode set to 100644
File size: 19.2 KB
Line 
1/****************************************************************************
2** ui.h extension file, included from the uic-generated form implementation.
3**
4** If you want to add, delete, or rename functions or slots, use
5** Qt Designer to update this file, preserving your code.
6**
7** You should not define a constructor or destructor in this file.
8** Instead, write your code in functions called init() and destroy().
9** These will automatically be called by the form's constructor and
10** destructor.
11*****************************************************************************/
12#include <fcntl.h>
13#include <QDateTime>
14#include <QDebug>
15#include <QDir>
16#include <QProcess>
17#include <QProgressDialog>
18#include <QSocketNotifier>
19#include <QString>
20#include <QTextStream>
21#include <pcbsd-utils.h>
22#include "mainWin.h"
23#include "../config.h"
24
25void mainWin::ProgramInit(QString ch, QString ip)
26{
27  // Set any warden directories
28  groupDetails->setVisible(false);
29  doingUpdate=false;
30  lastError="";
31  wDir = ch;
32  wIP = ip;
33  if ( ! wDir.isEmpty() )
34     setWindowTitle(tr("Updates for Jail:") + " " + wIP );
35
36  //Grab the username
37  //username = QString::fromLocal8Bit(getenv("LOGNAME"));
38  connect(buttonRescan, SIGNAL(clicked()), this, SLOT(slotRescanUpdates()));
39  connect(pushInstallUpdates, SIGNAL(clicked()), this, SLOT(slotInstallClicked()));
40  connect(pushClose, SIGNAL(clicked()), this, SLOT(slotCloseClicked()));
41  connect(checkAll, SIGNAL(clicked()), this, SLOT(slotSelectAllClicked()));
42  connect(listViewUpdates, SIGNAL(itemClicked(QListWidgetItem *)),this,SLOT(slotListClicked()));
43  connect(listViewUpdates, SIGNAL(itemActivated(QListWidgetItem *)),this,SLOT(slotListClicked()));
44  connect(listViewUpdates, SIGNAL(itemChanged(QListWidgetItem *)),this,SLOT(slotListClicked()));
45  connect(listViewUpdates, SIGNAL(itemPressed(QListWidgetItem *)),this,SLOT(slotListClicked()));
46  connect(listViewUpdates, SIGNAL(itemDoubleClicked(QListWidgetItem *)),this,SLOT(slotListDoubleClicked(QListWidgetItem *)));
47  progressUpdate->setHidden(true);
48
49  QTimer::singleShot(100, this, SLOT(slotRescanUpdates() ) );
50}
51
52void mainWin::slotListDoubleClicked(QListWidgetItem *cItem)
53{
54  if ( listUpdates.at(listViewUpdates->row(cItem)).at(7).isEmpty() )
55     return;
56
57  QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
58  QString username = env.value( "LOGNAME" );
59
60
61  QString url = listUpdates.at(listViewUpdates->row(cItem)).at(7);
62  qDebug() << url;
63  system("su -m " + username.toLatin1() + " -c 'openwith " + url.toLatin1() + "' &"); 
64}
65
66//Check whether an update was selected to enable the button
67void mainWin::slotListClicked(){
68  bool found = false;
69
70  // Get the total number of updates
71  for (int z=0; z < listViewUpdates->count(); ++z)
72    if ( listViewUpdates->item(z)->checkState() == Qt::Checked )
73      found = true;
74
75  if(!found)
76    pushInstallUpdates->setEnabled(false);
77  else
78    pushInstallUpdates->setEnabled(true);
79
80
81  // See if we have description text to update
82  groupDetails->setVisible(false);
83  textDesc->setText(QString());
84  if ( ! listViewUpdates->currentItem() )
85     return;
86
87  int myRow = listViewUpdates->currentRow();
88
89  if ( listUpdates.at(myRow).at(1) == "FBSDUPDATE" ) {
90    groupDetails->setVisible(true);
91
92    QString desc;
93    for (int p=2; p < listUpdates.at(myRow).count(); p++)
94      desc += listUpdates.at(myRow).at(p) + "<br>";
95    textDesc->setText(desc);
96  }
97}
98
99bool mainWin::sanityCheck()
100{
101  bool haveUP, haveMU;
102  int sa = 0;
103  haveUP = false;
104  haveMU = false;
105
106  for (int z=0; z < listViewUpdates->count(); ++z) {
107    if ( listViewUpdates->item(z)->checkState() == Qt::Checked ) {
108      if ( listUpdates.at(z).at(1) == "PACKAGE") {
109        haveUP = true;
110      }
111      if ( listUpdates.at(z).at(1) == "PATCH") {
112        haveUP = true;
113        if ( listUpdates.at(z).at(5) == "YES" ) 
114          sa++;
115      }
116      if ( listUpdates.at(z).at(1) == "SYSUPDATE") {
117        haveMU = true;
118      }
119    }
120  }
121
122  if ( (haveMU && haveUP ) || sa > 1 ) {
123    QMessageBox::warning(this, tr("Update Conflict"), tr("More than one stand-alone update has been selected! Please unselect all other updates and try again."));
124    return false;
125  }
126
127  return true;
128}
129
130void mainWin::doUpdates()
131{
132  // Set our UI elements
133  progressUpdate->setHidden(false);
134
135  curUpdate = -1;
136  curUpdateIndex = 0;
137  totUpdate = 0;
138
139  // Get the total number of updates
140  for (int z=0; z < listViewUpdates->count(); ++z)
141    if ( listViewUpdates->item(z)->checkState() == Qt::Checked )
142      totUpdate++;
143
144  // Start our loop processing updates
145  slotUpdateLoop();
146}
147
148void mainWin::slotUpdateLoop()
149{
150  QString tmp, tmp2, mUrl, PkgSet, Version, Arch;
151
152  // Check if the last update process finished
153  if ( curUpdate != -1 ) {
154    qDebug() << "Finished Update";
155    if ( uProc->exitStatus() != QProcess::NormalExit || uProc->exitCode() != 0)
156    {
157      // Read any remaining buffers
158      slotReadUpdateOutput();
159
160      // Warn user that this update failed
161      if ( lastError.isEmpty() )
162         QMessageBox::critical(this, tr("Update Failed!"), tr("Failed to install:") + listUpdates.at(curUpdate).at(0) + " " + tr("An unknown error occured!")); 
163      else
164         QMessageBox::critical(this, tr("Update Failed!"), tr("Failed to install:") + listUpdates.at(curUpdate).at(0) + " " + lastError); 
165    } else {
166      // If successfull system update download
167      if ( listUpdates.at(curUpdate).at(1) == "SYSUPDATE" )
168        QMessageBox::information(this, tr("Update Ready"), tr("Please reboot to start the update to PC-BSD version \"") + listUpdates.at(curUpdate).at(0) + "\". " + tr("This process may take a while, please do NOT interrupt the process.")); 
169    }
170
171    // Remove the lock file
172    if ( listUpdates.at(curUpdate).at(1) == "FBSDUPDATE" ) {
173        system("rm /tmp/.fbsdup-lock");
174    }
175
176    listViewUpdates->item(curUpdate)->setIcon(QIcon());
177    setWindowTitle(tr("Update Manager"));
178  }
179
180  // Start looking for the next update
181  for (int z=0; z < listViewUpdates->count(); ++z) {
182    if ( listViewUpdates->item(z)->checkState() == Qt::Checked && curUpdate < z ) 
183    {
184       
185      // Check for a freebsd-update lock file
186      if ( listUpdates.at(z).at(1) == "FBSDUPDATE" ) {
187        if ( QFile::exists("/tmp/.fbsdup-lock") ) {
188          QMessageBox::critical(this, tr("Update Failed!"), tr("Could not run freebsd-update, another process is already running!")); 
189          slotUpdateFinished();
190          return;
191        } 
192        // Lock out freebsd-update
193        system("touch /tmp/.fbsdup-lock");
194      }
195
196      curUpdate = z;
197      curUpdateIndex++;
198      progressUpdate->setHidden(false);
199      progressUpdate->setRange(0, 0);
200      tmp.setNum(curUpdateIndex);
201      tmp2.setNum(totUpdate);
202      setWindowTitle(tr("Updating:") + " " + listUpdates.at(z).at(0));
203
204      textLabel->setText(tr("Starting Update: %1 (%2 of %3)")
205                         .arg(listUpdates.at(z).at(0))
206                         .arg(tmp)
207                         .arg(tmp2));
208
209      // Get the icon
210      listViewUpdates->item(z)->setIcon(QIcon(":images/current-item.png"));
211
212      // Get the install tag
213      QString tag;
214      if ( listUpdates.at(z).at(1) == "SYSUPDATE" )
215        tag = listUpdates.at(z).at(4);
216      if ( listUpdates.at(z).at(1) == "PATCH" )
217        tag = listUpdates.at(z).at(3);
218
219      // Show tray that we are doing a download
220      QFile sysTrig( SYSTRIGGER );
221      if ( sysTrig.open( QIODevice::WriteOnly ) ) {
222        QTextStream streamTrig( &sysTrig );
223        streamTrig << "DOWNLOADING: ";
224      }
225
226      // Setup the upgrade process
227      uProc = new QProcess();
228      QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
229      env.insert("PCFETCHGUI", "YES");
230      uProc->setProcessEnvironment(env);
231      uProc->setProcessChannelMode(QProcess::MergedChannels);
232
233      // Connect the slots
234      connect( uProc, SIGNAL(readyReadStandardOutput()), this, SLOT(slotReadUpdateOutput()) );
235      connect( uProc, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(slotUpdateLoop()) );
236
237      // If doing FreeBSD Update run freebsd-update cmd
238      if ( wDir.isEmpty() ) {
239         if ( listUpdates.at(z).at(1) == "FBSDUPDATE" ) {
240           uProc->start("freebsd-update", QStringList() << "install"); 
241         } else {
242           uProc->start("pc-updatemanager", QStringList() << "install" << tag ); 
243         }
244      } else {
245         // Doing a warden update in a chroot environment
246         if ( listUpdates.at(z).at(1) == "FBSDUPDATE" ) {
247           uProc->start("chroot", QStringList() << wDir << "freebsd-update" << "install"); 
248         } 
249      }
250      qDebug() << "Update started";
251      return;
252    }
253
254  }
255
256  // If we get here, no more updates to do
257  slotUpdateFinished();
258}
259
260void mainWin::slotReadUpdateOutput()
261{
262  QString line, cI, tI, tmp;
263  bool ok, ok2;
264  cI.setNum(curUpdateIndex);
265  tI.setNum(totUpdate);
266
267
268  while (uProc->canReadLine()) {
269    line = uProc->readLine().simplified();
270
271    if ( line.indexOf("FETCH:") == 0 ) {
272      tmp = line;
273      tmp = tmp.remove(0, tmp.lastIndexOf("/") + 1);
274      textLabel->setText(tr("Downloading: %1 (Update %2 of %3)")
275                         .arg(tmp)
276                         .arg(cI)
277                         .arg(tI));
278      continue;
279    }
280   
281    if ( line.indexOf("SIZE:") == 0 ) {
282      line.section(" ", 1,1).toInt(&ok);
283      line.section(" ", 3,3).toInt(&ok2);
284      if ( ok && ok2 ) {
285        progressUpdate->setRange(0, line.section(" ", 1,1).toInt(&ok));
286        progressUpdate->setValue(line.section(" ", 3,3).toInt(&ok));
287      }
288      continue;
289    }
290    if ( line.indexOf("FETCHDONE") == 0 ) {
291      progressUpdate->setRange(0, 0);
292      textLabel->setText(tr("Updating: %1 (%2 of %3)")
293                         .arg(listUpdates.at(curUpdate).at(0))
294                         .arg(cI)
295                         .arg(tI));
296      continue;
297    }
298
299    if ( line.indexOf("TOTALSTEPS:") == 0 ) {
300      line.section(" ", 1,1).toInt(&ok);
301      if ( ok )
302        progressUpdate->setRange(0, line.section(" ", 1, 1).toInt(&ok));
303      continue;
304    }
305    if ( line.indexOf("SETSTEPS:") == 0 ) {
306      line.section(" ", 1,1).toInt(&ok);
307      if ( ok )
308        progressUpdate->setValue(line.section(" ", 1, 1).toInt(&ok));
309      continue;
310    }
311    if ( line.indexOf("ERROR:") == 0 ) {
312       lastError = line;
313       continue; 
314    }
315    qDebug() << line;
316  }
317}
318
319void mainWin::slotUpdateFinished()
320{
321  qDebug() << "Updates all Finished";
322  QFile sysTrig( SYSTRIGGER );
323  if ( sysTrig.open( QIODevice::WriteOnly ) ) {
324    QTextStream streamTrig( &sysTrig );
325     streamTrig << "INSTALLFINISHED: ";
326  }
327  sysTrig.close();
328
329  progressUpdate->setHidden(true);
330  slotRescanUpdates();
331}
332
333void mainWin::slotInstallClicked()
334{
335  // Sanity check our install choices
336  if (!sanityCheck())
337    return;
338
339  // Start the installation
340  doUpdates();
341}
342
343void mainWin::slotSelectAllClicked()
344{
345  for (int z=0; z < listViewUpdates->count(); ++z) {
346    listViewUpdates->item(z)->setCheckState(checkAll->checkState());   
347  }
348
349  slotListClicked();
350}
351
352void mainWin::slotRescanUpdates()
353{
354  if ( doingUpdate )
355     return;
356  groupDetails->setVisible(false);
357  groupUpdates->setEnabled(false);
358  listUpdates.clear();
359  textLabel->setText(tr("Checking for updates... Please Wait..."));
360  slotReadUpdateData();
361  slotDisplayUpdates();
362  qDebug() << listUpdates;
363  //disable the "select all" checkbox if no updates available
364  if(listUpdates.isEmpty() ){
365    checkAll->setEnabled(false);
366  }
367  pushInstallUpdates->setEnabled(false); //disable the button until an update is selected
368  if ( ! doingUpdate )
369    groupUpdates->setEnabled(true);
370}
371
372void mainWin::slotDisplayUpdates()
373{
374  if ( ! doingUpdate )
375    groupUpdates->setEnabled(true);
376  listViewUpdates->clear();
377
378  // Check if the system has an upgrade available
379  if ( QFile::exists("/usr/local/tmp/update-stagedir/doupdate.sh") ) {
380    textLabel->setText(tr("A system upgrade is waiting to be installed. Please reboot to begin!"));
381    return;
382  }
383
384
385  // Any system updates?
386  if ( listUpdates.isEmpty() ) {
387    textLabel->setText(tr("Your system is fully updated!"));
388    groupUpdates->setTitle("");
389    groupUpdates->setEnabled(true);
390    return;
391  }
392
393  textLabel->setText(tr("System updates available!"));
394  groupUpdates->setTitle(tr("Available Updates"));
395
396  // Start parsing the updates and list whats available
397  for (int z=0; z < listUpdates.count(); ++z) {
398    if ( listUpdates.at(z).at(1) == "SYSUPDATE" ) {
399      QListWidgetItem *item = new QListWidgetItem(tr("System Upgrade: %1 (%2)")
400                                                  .arg(listUpdates.at(z).at(2))
401                                                  .arg(listUpdates.at(z).at(3)));
402      item->setToolTip(tr("PC-BSD Version:") + "<br>" + listUpdates.at(z).at(2) + "<hr>" + tr("This update must be installed by itself.") + "<br>" + tr("Creating a backup of your data first is recommended."));
403      item->setCheckState(Qt::Unchecked);
404      listViewUpdates->addItem(item);
405    }
406    if ( listUpdates.at(z).at(1) == "PATCH" ) {
407      QListWidgetItem *item = new QListWidgetItem(tr("Patch: %1 (%2)")
408                                                  .arg(listUpdates.at(z).at(0))
409                                                  .arg(listUpdates.at(z).at(2)));
410      item->setCheckState(Qt::Unchecked);
411      item->setToolTip(tr("This is a patch for your version of PC-BSD") + "<hr>" + tr("Patch Size:") + " " + listUpdates.at(z).at(4) + "MB<br>");
412      listViewUpdates->addItem(item);
413    }
414
415    if ( listUpdates.at(z).at(1) == "FBSDUPDATE" ) {
416      QString fileNameList;
417      for (int p=2; p < listUpdates.at(z).count(); p++)
418        fileNameList += listUpdates.at(z).at(p) + "<br>";
419
420      QListWidgetItem *item = new QListWidgetItem(tr("Base System Updates"));
421      item->setCheckState(Qt::Unchecked);
422      item->setToolTip(fileNameList);
423     
424      listViewUpdates->addItem(item);
425    }
426
427    if ( listUpdates.at(z).at(1) == "PACKAGE" ) {
428      QString pkgNameList;
429      for (int p=2; p < listUpdates.at(z).count(); p=p+5) {
430        if ( listUpdates.at(z).count() < p + 4 ) 
431          break;
432
433        pkgNameList += listUpdates.at(z).at((p)) + " " + listUpdates.at(z).at((p+1)) + " -> " + listUpdates.at(z).at((p+2)) + "<br>";
434      }
435      QListWidgetItem *item = new QListWidgetItem(tr("System Package Updates"));
436      item->setCheckState(Qt::Unchecked);
437      item->setToolTip(tr("The following package updates are available:") + "<hr>" + pkgNameList);
438     
439      listViewUpdates->addItem(item);
440    }
441  }
442}
443
444void mainWin::slotReadUpdateData()
445{
446  // If on the base system, check for PC-BSD updates
447  if ( wDir.isEmpty() )
448    checkPCUpdates();
449
450  // Check for FreeBSD Updates now
451  checkFBSDUpdates();
452
453}
454
455void mainWin::checkPCUpdates() {
456
457  QString line, tmp, name, type, version, date, tag, url, size, sa, rr;
458  QStringList up;
459
460  QProcess p;
461  p.start(QString("pc-updatemanager"), QStringList() << "check");
462  while(p.state() == QProcess::Starting || p.state() == QProcess::Running)
463     QCoreApplication::processEvents();
464
465  while (p.canReadLine()) {
466    line = p.readLine().simplified();
467    if ( line.indexOf("NAME: ") == 0) {
468       name = line.replace("NAME: ", "");
469       continue;
470    }
471    if ( line.indexOf("TYPE: ") == 0) {
472       type = line.replace("TYPE: ", "");
473       continue;
474    }
475
476    if ( type == "SYSUPDATE" ) {
477      if ( line.indexOf("VERSION: ") == 0) {
478         version = line.replace("VERSION: ", "");
479         continue;
480      }
481      if ( line.indexOf("DATE: ") == 0) {
482         date = line.replace("DATE: ", "");
483         continue;
484      }
485      if ( line.indexOf("TAG: ") == 0) {
486         tag = line.replace("TAG: ", "");
487         continue;
488      }
489      if ( line.indexOf("DETAILS: ") == 0) {
490         url = line.replace("DETAILS: ", "");
491         continue;
492      }
493
494      if ( line.indexOf("To install:") == 0) {
495         up.clear();
496         up << name << type << version << date << tag << url;
497         listUpdates.append(up);
498         type=""; name="", version="", date="", tag="", url="";
499         continue;
500      }
501
502    }
503    if ( type == "PATCH" ) {
504      if ( line.indexOf("DATE: ") == 0) {
505         date = line.replace("DATE: ", "");
506         continue;
507      }
508      if ( line.indexOf("TAG: ") == 0) {
509         tag = line.replace("TAG: ", "");
510         continue;
511      }
512      if ( line.indexOf("SIZE: ") == 0) {
513         size = line.replace("SIZE: ", "");
514         continue;
515      }
516      if ( line.indexOf("STANDALONE: ") == 0) {
517         sa = line.replace("STANDALONE: ", "");
518         continue;
519      }
520      if ( line.indexOf("REQUIRESREBOOT: ") == 0) {
521         rr = line.replace("REQUIRESREBOOT: ", "");
522         continue;
523      }
524      if ( line.indexOf("DETAILS: ") == 0) {
525         url = line.replace("DETAILS: ", "");
526         continue;
527      }
528      if ( line.indexOf("To install:") == 0) {
529         // TODO add this update to list
530         up.clear();
531         up << name << type << date << tag << size << sa << rr << url;
532         listUpdates.append(up);
533         type=""; name="", date="", tag="", size="", sa="", rr="", url="";
534         continue;
535      }
536    }
537
538  }
539
540
541}
542
543void mainWin::checkFBSDUpdates() {
544  QString line, toPatchVer, tmp;
545  QStringList up, listDesc, listPkgs;
546
547  if ( QFile::exists("/tmp/.fbsdup-lock") ) {
548     qDebug() << "Skipping update check - freebsd-update is running elsewhere";
549     return;
550  }
551
552  // Now check if there are freebsd-updates to install
553  QProcess f;
554  if ( wDir.isEmpty() )
555     f.start(QString("pc-fbsdupdatecheck"), QStringList());
556  else {
557     QProcess::execute("cp /usr/local/bin/pc-fbsdupdatecheck " + wDir + "/tmp/.fbupdatechk");
558     QProcess::execute("chmod 755 " + wDir + "/tmp/.fbupdatechk");
559     f.start(QString("chroot"), QStringList() << wDir << "/tmp/.fbupdatechk" << "fetch" );
560  }
561  while(f.state() == QProcess::Starting || f.state() == QProcess::Running)
562     QCoreApplication::processEvents();
563
564  bool fUp = false;
565 
566  while (f.canReadLine()) {
567    line = f.readLine().simplified();
568    qDebug() << line;
569    if ( line.indexOf("The following files will be ") == 0) {
570       toPatchVer= line.remove(0, line.lastIndexOf(" "));
571       toPatchVer=toPatchVer.section("-", 2,2);
572       toPatchVer=toPatchVer.section(":", 0,0);
573       toPatchVer=toPatchVer.section("p", 1,1);
574       fUp = true;
575       listPkgs << " " << tr("The following files will be updated:");
576       continue;
577    }
578
579    if ( fUp )
580       listPkgs << line;
581  }
582
583  if ( ! wDir.isEmpty() )
584     QProcess::execute("rm " + wDir + "/tmp/.fbupdatechk");
585
586  // Are there freebsd updates to install?
587  if ( fUp ) {
588    QString mySysVer;
589    QString myPatchVer;
590
591    // Lets try and fetch the desc file
592    QProcess::execute("fetch -o /tmp/.fbsdupdesc http://fbsd-update.pcbsd.org/updates.desc");
593
594    // Get the current system ver
595    QProcess p;
596    p.start(QString("uname"), QStringList() << "-r");
597    while(p.state() == QProcess::Starting || p.state() == QProcess::Running)
598       QCoreApplication::processEvents();
599    tmp = p.readLine().simplified();
600    mySysVer = tmp;
601    myPatchVer = tmp;
602    mySysVer = mySysVer.section("-", 0, 1);
603    mySysVer = mySysVer.section("-", 0, 1);
604    myPatchVer = myPatchVer.section("-", 2, 2);
605    myPatchVer = myPatchVer.section(":", 0, 0);
606    myPatchVer = myPatchVer.section("p", 1, 1);
607
608    QFile file("/tmp/.fbsdupdesc");
609    if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
610        listDesc << tr("Update Details:");
611      while (!file.atEnd()) {
612         line = file.readLine();
613         tmp = line;
614         if ( tmp.section(":::", 0, 0) != mySysVer ) 
615            continue;
616         if ( tmp.section(":::", 1, 1) <= myPatchVer )
617            continue;
618         if ( tmp.section(":::", 1, 1) > toPatchVer )
619            continue;
620         listDesc << tmp.section(":::", 2, 2);
621      }
622    }
623
624    up.clear();
625    up << "FreeBSD Security Updates" << "FBSDUPDATE";
626    up.append(listDesc + listPkgs);
627    listUpdates.append(up);
628  }
629
630}
631
632void mainWin::slotSingleInstance() {
633   this->hide();
634   this->showNormal();
635   this->activateWindow();
636   this->raise();
637}
638
639void mainWin::slotCloseClicked() {
640   close();
641}
642
Note: See TracBrowser for help on using the repository browser.