aboutsummaryrefslogtreecommitdiff
path: root/src-qt5/desktop-utils/lumina-archiver/imgDialog.cpp
blob: c353b751591be311761b8f995d7974374aff37f6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
//===========================================
//  Lumina-Desktop source code
//  Copyright (c) 2016, Ken Moore
//  Available under the 3-clause BSD license
//  See the LICENSE file for full details
//===========================================
#include "imgDialog.h"
#include "ui_imgDialog.h"

#include <QMessageBox>

#include <LuminaOS.h>
#include <LuminaXDG.h>

#include <unistd.h>
#include <sys/types.h>
#include <signal.h>

imgDialog::imgDialog(QWidget *parent) : QDialog(parent), ui(new Ui::imgDialog()){
  ui->setupUi(this); //load the designer form
  QString title = tr("Burn Disk Image to Device");
  if( getuid()==0){ title.append(" ("+tr("Admin Mode")+")"); }
  this->setWindowTitle(title);
  ui->frame_running->setVisible(false);
  ui->frame_setup->setVisible(true);
  ddProc = 0;
  unitdiv = 1;
  //Setup the signals/slots
  ui->tool_refresh->setIcon( LXDG::findIcon("view-refresh","drive-removable-media-usb-pendrive") );
  connect(ui->push_cancel, SIGNAL(clicked()), this, SLOT(cancel()) );
  connect(ui->push_start, SIGNAL(clicked()), this, SLOT(start_process()) );
  connect(ui->tool_refresh, SIGNAL(clicked()), this, SLOT(loadDeviceList()) );
  loadDeviceList(); //do the initial load of the available devices
  //Setup the possible transfer rate units
  ui->combo_rate_units->clear();
  ui->combo_rate_units->addItem(tr("Kilobyte(s)"), "k");
  ui->combo_rate_units->addItem(tr("Megabyte(s)"), "m");
  ui->combo_rate_units->addItem(tr("Gigabyte(s)"), "g");
  ui->combo_rate_units->setCurrentIndex(1); //MB
  //Setup the Process Timer
  procTimer = new QTimer(this);
    procTimer->setInterval(1000); //1 second updates
  connect(procTimer, SIGNAL(timeout()), this, SLOT(getProcStatus()) );
  //Determine which type of system this is for the process status signal
  BSD_signal = LOS::OSName().contains("BSD"); //assume everything else is Linux-like
}

imgDialog::~imgDialog(){

}

void imgDialog::loadIMG(QString filepath){
  ui->label_iso->setText(filepath.section("/",-1)); //only show the filename
  ui->label_iso->setWhatsThis(filepath); //save the full path for later
}

//============================
//  PRIVATE SLOTS
//============================
void imgDialog::start_process(){
  //Sanity Checks
  if( !QFile::exists(ui->combo_devices->currentData().toString()) ){ loadDeviceList(); return; } //USB device no longer available
  if(!QFile::exists(ui->label_iso->whatsThis()) ){ return; } //IMG file no longer available
  qDebug() << "Start Process...";
  //Read the size of the img file
  QString units = ui->combo_rate_units->currentData().toString();
  if(units=="k"){ unitdiv = 1024; }
  else if(units=="m"){ unitdiv = 1024*1024; }
  else if(units=="g"){ unitdiv = 1024*1024*1024; }
  qint64 bytes = QFileInfo(ui->label_iso->whatsThis()).size();
  //qDebug() << "IMG File size:" << bytes;
  //Set the progressBar maximum
  ui->progressBar->setRange(0, qRound(bytes/unitdiv) );
  ui->progressBar->setValue(0);
  ui->label_dev->setText( ui->combo_devices->currentText() );
  ui->label_time->setText("0:00");
  ui->frame_running->setVisible(true);
  ui->frame_setup->setVisible(false);
  //qDebug() << "Blocks:" << ui->progressBar->maximum();
  //Initialize the process
  if(ddProc==0){
    ddProc = new QProcess(this);
    connect(ddProc, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(procFinished()) );
    connect(ddProc, SIGNAL(readyRead()), this, SLOT(procInfoAvailable()) );
    ddProc->setProcessChannelMode(QProcess::MergedChannels);
  }
  //Generate the command
  QString prog; QStringList args;
  //if( ::getuid()!=0){ prog = "qsudo";  args<<"dd"; }
  //else{ 
    prog = "dd"; 
  //}
   args << "if="+ui->label_iso->whatsThis();
   args << "of="+ui->combo_devices->currentData().toString();
   args << "bs="+QString::number(ui->spin_rate_num->value())+units;
   if(ui->check_sync->isChecked()){ args << "conv=sync"; }
  //Start the process
  startTime = QDateTime::currentDateTime();
  ddProc->start(prog, args);
  //Start the timer to watch for updates
  procTimer->start();
  ui->push_start->setEnabled(false);
}

void imgDialog::cancel(){
  if(ddProc==0 || ddProc->state()==QProcess::NotRunning){
    this->close();
  }else{
    //Prompt if the transfer should be cancelled
    if(QMessageBox::Yes == QMessageBox::question(this, tr("Cancel Image Burn?"), tr("Do you wish to stop the current disk image burn process?")+"\n\n"+tr("Warning: This will leave the USB device in an inconsistent state")) ){
      ddProc->kill();
    }
  }
}

void imgDialog::loadDeviceList(){
  ui->combo_devices->clear();
  //Probe the system for USB devices
  QDir devDir("/dev");
  QString filter = (BSD_signal) ? "da*" : "sd*";
  QStringList usb = devDir.entryList(QStringList() << filter, QDir::System, QDir::Name);
  //Filter out any devices which are currently mounted/used

  //Create the list
  for(int i=0; i<usb.length(); i++){
    ui->combo_devices->addItem(usb[i], devDir.absoluteFilePath(usb[i]));
  }
}

void imgDialog::getProcStatus(){
  if(ddProc==0 || ddProc->state()!=QProcess::Running ){ return; }
  QStringList pidlist = LUtils::getCmdOutput("pgrep -S -f \"dd if="+ui->label_iso->whatsThis()+"\"");
  if(pidlist.isEmpty()){ return; }
  int pid = pidlist.first().simplified().toInt(); //just use the first pid - the pgrep should be detailed enough to only match one
  //qDebug() << "Sending signal to show status on PID:" << pid;
#ifndef __linux__
  ::kill(pid, SIGINFO); //On BSD systems, the INFO signal is used to poke dd for status updates
#else
  //LINUX systems do not even have a SIGINFO defined - need to block this off with defines...
  ::kill(pid, SIGUSR1);  //On linux systems, the USR1 signal is used to poke dd for status updates
#endif
  //Now update the elapsed time on the UI
    int elapsed = startTime.secsTo( QDateTime::currentDateTime() );
    int min = elapsed/60;
    int secs = elapsed%60;
    ui->label_time->setText( QString::number(min)+":"+ (secs < 10 ? "0" : "")+QString::number(secs) );
}

void imgDialog::procInfoAvailable(){
  lastmsg = ddProc->readAll();
  if(lastmsg.endsWith("\n")){ lastmsg.chop(1); }
  //qDebug() << "Got Process Info:" << lastmsg;
  //Now look for the " bytes transferred" line
  QStringList records = lastmsg.split("\n").filter(" bytes transferred ");
  if(!records.isEmpty()){
    //Update the progress bar
    //qDebug() << "Got status update:" << records.last();
    ui->progressBar->setValue( qRound(records.last().section(" bytes",0,0).toDouble()/unitdiv) );
  }
}

void imgDialog::procFinished(){
  qDebug() << "Process Finished:" << ddProc->exitCode();
  procTimer->stop();
  ui->frame_running->setVisible(false);
  ui->frame_setup->setVisible(true);
  if(ddProc->exitStatus()==QProcess::NormalExit){
    if(ddProc->exitCode() !=0 ){
      if(lastmsg.contains("permission denied", Qt::CaseInsensitive) && LUtils::isValidBinary("qsudo") ){
        if(QMessageBox::Yes == QMessageBox::question(this, tr("Administrator Permissions Needed"), tr("This operation requires administrator priviledges.")+"\n\n"+tr("Would you like to enable these priviledges?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) ){
          QProcess::startDetached("qsudo", QStringList() << "lumina-archiver" << "--burn-img" << ui->label_iso->whatsThis());
          exit(0);
        }
      }else{
        QMessageBox::warning(this, tr("ERROR"), tr("The process could not be completed:")+"\n\n"+lastmsg);
      }
    }else{
      QMessageBox::information(this, tr("SUCCESS"), tr("The image was successfully burned to the device") );
      this->close();
    }
  }
}
bgstack15