source: src-qt4/pc-updategui/mainWin.cpp @ 408fff7

9.2-releasereleng/10.0releng/10.0.1releng/10.0.2
Last change on this file since 408fff7 was 408fff7, checked in by Kris Moore <kris@…>, 13 months ago

Update the Update GUI to use a new Details section, which gives descriptions of
updates pulled down from freebsd-update

  • Property mode set to 100644
File size: 18.5 KB
RevLine 
[1620346]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
[408fff7]28  groupDetails->setVisible(false);
[1620346]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);
[408fff7]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  }
[1620346]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    listViewUpdates->item(curUpdate)->setIcon(QIcon());
172    setWindowTitle(tr("Update Manager"));
173  }
174
175  // Start looking for the next update
176  for (int z=0; z < listViewUpdates->count(); ++z) {
177    if ( listViewUpdates->item(z)->checkState() == Qt::Checked && curUpdate < z ) 
178    {
179      curUpdate = z;
180      curUpdateIndex++;
181      progressUpdate->setHidden(false);
182      progressUpdate->setRange(0, 0);
183      tmp.setNum(curUpdateIndex);
184      tmp2.setNum(totUpdate);
185      setWindowTitle(tr("Updating:") + " " + listUpdates.at(z).at(0));
186
187      textLabel->setText(tr("Starting Update: %1 (%2 of %3)")
188                         .arg(listUpdates.at(z).at(0))
189                         .arg(tmp)
190                         .arg(tmp2));
191
192      // Get the icon
193      listViewUpdates->item(z)->setIcon(QIcon(":images/current-item.png"));
194
195      // Get the install tag
196      QString tag;
197      if ( listUpdates.at(z).at(1) == "SYSUPDATE" )
198        tag = listUpdates.at(z).at(4);
199      if ( listUpdates.at(z).at(1) == "PATCH" )
200        tag = listUpdates.at(z).at(3);
201
202      // Show tray that we are doing a download
203      QFile sysTrig( SYSTRIGGER );
204      if ( sysTrig.open( QIODevice::WriteOnly ) ) {
205        QTextStream streamTrig( &sysTrig );
206        streamTrig << "DOWNLOADING: ";
207      }
208
209      // Setup the upgrade process
210      uProc = new QProcess();
211      QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
212      env.insert("PCFETCHGUI", "YES");
213      uProc->setProcessEnvironment(env);
214      uProc->setProcessChannelMode(QProcess::MergedChannels);
215
216      // Connect the slots
217      connect( uProc, SIGNAL(readyReadStandardOutput()), this, SLOT(slotReadUpdateOutput()) );
218      connect( uProc, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(slotUpdateLoop()) );
219
220      // If doing FreeBSD Update run freebsd-update cmd
221      if ( wDir.isEmpty() ) {
222         if ( listUpdates.at(z).at(1) == "FBSDUPDATE" ) {
223           uProc->start("freebsd-update", QStringList() << "install"); 
224         } else {
225           uProc->start("pc-updatemanager", QStringList() << "install" << tag ); 
226         }
227      } else {
228         // Doing a warden update in a chroot environment
229         if ( listUpdates.at(z).at(1) == "FBSDUPDATE" ) {
230           uProc->start("chroot", QStringList() << wDir << "freebsd-update" << "install"); 
231         } 
232      }
233      qDebug() << "Update started";
234      return;
235    }
236
237  }
238
239  // If we get here, no more updates to do
240  slotUpdateFinished();
241}
242
243void mainWin::slotReadUpdateOutput()
244{
245  QString line, cI, tI, tmp;
246  bool ok, ok2;
247  cI.setNum(curUpdateIndex);
248  tI.setNum(totUpdate);
249
250
251  while (uProc->canReadLine()) {
252    line = uProc->readLine().simplified();
253
254    if ( line.indexOf("FETCH:") == 0 ) {
255      tmp = line;
256      tmp = tmp.remove(0, tmp.lastIndexOf("/") + 1);
257      textLabel->setText(tr("Downloading: %1 (Update %2 of %3)")
258                         .arg(tmp)
259                         .arg(cI)
260                         .arg(tI));
261      continue;
262    }
263   
264    if ( line.indexOf("SIZE:") == 0 ) {
265      line.section(" ", 1,1).toInt(&ok);
266      line.section(" ", 3,3).toInt(&ok2);
267      if ( ok && ok2 ) {
268        progressUpdate->setRange(0, line.section(" ", 1,1).toInt(&ok));
269        progressUpdate->setValue(line.section(" ", 3,3).toInt(&ok));
270      }
271      continue;
272    }
273    if ( line.indexOf("FETCHDONE") == 0 ) {
274      progressUpdate->setRange(0, 0);
275      textLabel->setText(tr("Updating: %1 (%2 of %3)")
276                         .arg(listUpdates.at(curUpdate).at(0))
277                         .arg(cI)
278                         .arg(tI));
279      continue;
280    }
281
282    if ( line.indexOf("TOTALSTEPS:") == 0 ) {
283      line.section(" ", 1,1).toInt(&ok);
284      if ( ok )
285        progressUpdate->setRange(0, line.section(" ", 1, 1).toInt(&ok));
286      continue;
287    }
288    if ( line.indexOf("SETSTEPS:") == 0 ) {
289      line.section(" ", 1,1).toInt(&ok);
290      if ( ok )
291        progressUpdate->setValue(line.section(" ", 1, 1).toInt(&ok));
292      continue;
293    }
294    if ( line.indexOf("ERROR:") == 0 ) {
295       lastError = line;
296       continue; 
297    }
298    qDebug() << line;
299  }
300}
301
302void mainWin::slotUpdateFinished()
303{
304  qDebug() << "Updates all Finished";
305  QFile sysTrig( SYSTRIGGER );
306  if ( sysTrig.open( QIODevice::WriteOnly ) ) {
307    QTextStream streamTrig( &sysTrig );
308     streamTrig << "INSTALLFINISHED: ";
309  }
310  sysTrig.close();
311
312  progressUpdate->setHidden(true);
313  slotRescanUpdates();
314}
315
316void mainWin::slotInstallClicked()
317{
318  // Sanity check our install choices
319  if (!sanityCheck())
320    return;
321
322  // Start the installation
323  doUpdates();
324}
325
326void mainWin::slotSelectAllClicked()
327{
328  for (int z=0; z < listViewUpdates->count(); ++z) {
329    listViewUpdates->item(z)->setCheckState(checkAll->checkState());   
330  }
331
332  slotListClicked();
333}
334
335void mainWin::slotRescanUpdates()
336{
337  if ( doingUpdate )
338     return;
339  groupUpdates->setEnabled(false);
340  listUpdates.clear();
341  textLabel->setText(tr("Checking for updates... Please Wait..."));
342  slotReadUpdateData();
343  slotDisplayUpdates();
344  qDebug() << listUpdates;
345  //disable the "select all" checkbox if no updates available
346  if(listUpdates.isEmpty() ){
347    checkAll->setEnabled(false);
348  }
349  pushInstallUpdates->setEnabled(false); //disable the button until an update is selected
350  if ( ! doingUpdate )
351    groupUpdates->setEnabled(true);
352}
353
354void mainWin::slotDisplayUpdates()
355{
356  if ( ! doingUpdate )
[67e6e29]357    groupUpdates->setEnabled(true);
[1620346]358  listViewUpdates->clear();
359
360  // Check if the system has an upgrade available
361  if ( QFile::exists("/usr/local/tmp/update-stagedir/doupdate.sh") ) {
362    textLabel->setText(tr("A system upgrade is waiting to be installed. Please reboot to begin!"));
363    return;
364  }
365
366
367  // Any system updates?
368  if ( listUpdates.isEmpty() ) {
[67e6e29]369    textLabel->setText(tr("Your system is fully updated!"));
[1620346]370    groupUpdates->setTitle("");
[67e6e29]371    groupUpdates->setEnabled(true);
[1620346]372    return;
373  }
374
375  textLabel->setText(tr("System updates available!"));
376  groupUpdates->setTitle(tr("Available Updates"));
377
378  // Start parsing the updates and list whats available
379  for (int z=0; z < listUpdates.count(); ++z) {
380    if ( listUpdates.at(z).at(1) == "SYSUPDATE" ) {
381      QListWidgetItem *item = new QListWidgetItem(tr("System Upgrade: %1 (%2)")
382                                                  .arg(listUpdates.at(z).at(2))
383                                                  .arg(listUpdates.at(z).at(3)));
384      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."));
385      item->setCheckState(Qt::Unchecked);
386      listViewUpdates->addItem(item);
387    }
388    if ( listUpdates.at(z).at(1) == "PATCH" ) {
389      QListWidgetItem *item = new QListWidgetItem(tr("Patch: %1 (%2)")
390                                                  .arg(listUpdates.at(z).at(0))
391                                                  .arg(listUpdates.at(z).at(2)));
392      item->setCheckState(Qt::Unchecked);
393      item->setToolTip(tr("This is a patch for your version of PC-BSD") + "<hr>" + tr("Patch Size:") + " " + listUpdates.at(z).at(4) + "MB<br>");
394      listViewUpdates->addItem(item);
395    }
396
397    if ( listUpdates.at(z).at(1) == "FBSDUPDATE" ) {
398      QString fileNameList;
399      for (int p=2; p < listUpdates.at(z).count(); p++)
400        fileNameList += listUpdates.at(z).at(p) + "<br>";
401
[408fff7]402      QListWidgetItem *item = new QListWidgetItem(tr("Base System Updates"));
[1620346]403      item->setCheckState(Qt::Unchecked);
[408fff7]404      item->setToolTip(fileNameList);
[1620346]405     
406      listViewUpdates->addItem(item);
407    }
408
409    if ( listUpdates.at(z).at(1) == "PACKAGE" ) {
410      QString pkgNameList;
411      for (int p=2; p < listUpdates.at(z).count(); p=p+5) {
412        if ( listUpdates.at(z).count() < p + 4 ) 
413          break;
414
415        pkgNameList += listUpdates.at(z).at((p)) + " " + listUpdates.at(z).at((p+1)) + " -> " + listUpdates.at(z).at((p+2)) + "<br>";
416      }
417      QListWidgetItem *item = new QListWidgetItem(tr("System Package Updates"));
418      item->setCheckState(Qt::Unchecked);
419      item->setToolTip(tr("The following package updates are available:") + "<hr>" + pkgNameList);
420     
421      listViewUpdates->addItem(item);
422    }
423  }
424}
425
426void mainWin::slotReadUpdateData()
427{
428  // If on the base system, check for PC-BSD updates
429  if ( wDir.isEmpty() )
430    checkPCUpdates();
431
432  // Check for FreeBSD Updates now
433  checkFBSDUpdates();
434
435}
436
437void mainWin::checkPCUpdates() {
438
439  QString line, tmp, name, type, version, date, tag, url, size, sa, rr;
[67e6e29]440  QStringList up;
[1620346]441
442  QProcess p;
443  p.start(QString("pc-updatemanager"), QStringList() << "check");
444  while(p.state() == QProcess::Starting || p.state() == QProcess::Running)
445     QCoreApplication::processEvents();
446
447  while (p.canReadLine()) {
448    line = p.readLine().simplified();
449    if ( line.indexOf("NAME: ") == 0) {
450       name = line.replace("NAME: ", "");
451       continue;
452    }
453    if ( line.indexOf("TYPE: ") == 0) {
454       type = line.replace("TYPE: ", "");
455       continue;
456    }
457
458    if ( type == "SYSUPDATE" ) {
459      if ( line.indexOf("VERSION: ") == 0) {
460         version = line.replace("VERSION: ", "");
461         continue;
462      }
463      if ( line.indexOf("DATE: ") == 0) {
464         date = line.replace("DATE: ", "");
465         continue;
466      }
467      if ( line.indexOf("TAG: ") == 0) {
468         tag = line.replace("TAG: ", "");
469         continue;
470      }
471      if ( line.indexOf("DETAILS: ") == 0) {
472         url = line.replace("DETAILS: ", "");
473         continue;
474      }
475
476      if ( line.indexOf("To install:") == 0) {
477         up.clear();
478         up << name << type << version << date << tag << url;
479         listUpdates.append(up);
480         type=""; name="", version="", date="", tag="", url="";
481         continue;
482      }
483
484    }
485    if ( type == "PATCH" ) {
486      if ( line.indexOf("DATE: ") == 0) {
487         date = line.replace("DATE: ", "");
488         continue;
489      }
490      if ( line.indexOf("TAG: ") == 0) {
491         tag = line.replace("TAG: ", "");
492         continue;
493      }
494      if ( line.indexOf("SIZE: ") == 0) {
495         size = line.replace("SIZE: ", "");
496         continue;
497      }
498      if ( line.indexOf("STANDALONE: ") == 0) {
499         sa = line.replace("STANDALONE: ", "");
500         continue;
501      }
502      if ( line.indexOf("REQUIRESREBOOT: ") == 0) {
503         rr = line.replace("REQUIRESREBOOT: ", "");
504         continue;
505      }
506      if ( line.indexOf("DETAILS: ") == 0) {
507         url = line.replace("DETAILS: ", "");
508         continue;
509      }
510      if ( line.indexOf("To install:") == 0) {
511         // TODO add this update to list
512         up.clear();
513         up << name << type << date << tag << size << sa << rr << url;
514         listUpdates.append(up);
515         type=""; name="", date="", tag="", size="", sa="", rr="", url="";
516         continue;
517      }
518    }
519
520  }
521
522
523}
524
525void mainWin::checkFBSDUpdates() {
[408fff7]526  QString line, toPatchVer, tmp;
527  QStringList up, listDesc, listPkgs;
[1620346]528
529  // Now check if there are freebsd-updates to install
530  QProcess f;
531  if ( wDir.isEmpty() )
532     f.start(QString("pc-fbsdupdatecheck"), QStringList());
533  else {
534     QProcess::execute("cp /usr/local/bin/pc-fbsdupdatecheck " + wDir + "/tmp/.fbupdatechk");
535     QProcess::execute("chmod 755 " + wDir + "/tmp/.fbupdatechk");
536     f.start(QString("chroot"), QStringList() << wDir << "/tmp/.fbupdatechk" << "fetch" );
537  }
538  while(f.state() == QProcess::Starting || f.state() == QProcess::Running)
539     QCoreApplication::processEvents();
540
541  bool fUp = false;
542 
543  while (f.canReadLine()) {
544    line = f.readLine().simplified();
545    qDebug() << line;
[408fff7]546    if ( line.indexOf("The following files will be ") == 0) {
547       toPatchVer= line.remove(0, line.lastIndexOf(" "));
548       toPatchVer=toPatchVer.section("-", 2,2);
549       toPatchVer=toPatchVer.section(":", 0,0);
550       toPatchVer=toPatchVer.section("p", 1,1);
[1620346]551       fUp = true;
[408fff7]552       listPkgs << " " << tr("The following files will be updated:");
[1620346]553       continue;
554    }
555
556    if ( fUp )
557       listPkgs << line;
558  }
559
560  if ( ! wDir.isEmpty() )
561     QProcess::execute("rm " + wDir + "/tmp/.fbupdatechk");
562
563  // Are there freebsd updates to install?
564  if ( fUp ) {
[408fff7]565    QString mySysVer;
566    QString myPatchVer;
567
568    // Lets try and fetch the desc file
569    QProcess::execute("fetch -o /tmp/.fbsdupdesc http://fbsd-update.pcbsd.org/updates.desc");
570
571    // Get the current system ver
572    QProcess p;
573    p.start(QString("uname"), QStringList() << "-r");
574    while(p.state() == QProcess::Starting || p.state() == QProcess::Running)
575       QCoreApplication::processEvents();
576    tmp = p.readLine().simplified();
577    mySysVer = tmp;
578    myPatchVer = tmp;
579    mySysVer = mySysVer.section("-", 0, 1);
580    mySysVer = mySysVer.section("-", 0, 1);
581    myPatchVer = myPatchVer.section("-", 2, 2);
582    myPatchVer = myPatchVer.section(":", 0, 0);
583    myPatchVer = myPatchVer.section("p", 1, 1);
584
585    QFile file("/tmp/.fbsdupdesc");
586    if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
587        listDesc << tr("Update Details:");
588      while (!file.atEnd()) {
589         line = file.readLine();
590         tmp = line;
591         if ( tmp.section(":::", 0, 0) != mySysVer ) 
592            continue;
593         if ( tmp.section(":::", 1, 1) <= myPatchVer )
594            continue;
595         if ( tmp.section(":::", 1, 1) > toPatchVer )
596            continue;
597         listDesc << tmp.section(":::", 2, 2);
598      }
599    }
600
[1620346]601    up.clear();
602    up << "FreeBSD Security Updates" << "FBSDUPDATE";
[408fff7]603    up.append(listDesc + listPkgs);
[1620346]604    listUpdates.append(up);
605  }
606
607}
608
609void mainWin::slotSingleInstance() {
610   this->hide();
611   this->showNormal();
612   this->activateWindow();
613   this->raise();
614}
615
616void mainWin::slotCloseClicked() {
617   close();
618}
619
Note: See TracBrowser for help on using the repository browser.