source: src-qt4/pc-mounttray/menuItem.cpp @ db3bc52

releng/10.0.1releng/10.0.2releng/10.0.3
Last change on this file since db3bc52 was db3bc52, checked in by Ken Moore <ken@…>, 8 months ago

Adjust the mount tray unmounting routine to first unmount any additional nullfs mounts of the original mountpoint before unmounting the mountpoint. This still needs testing on 10.0 (since 10.x PBI's do the additional nullfs mounts).

  • Property mode set to 100644
File size: 15.8 KB
Line 
1#include <pcbsd-utils.h>
2#include "menuItem.h"
3
4
5MenuItem::MenuItem(QWidget* parent, QString newdevice, QString newlabel, QString newtype, QString newfs, QString user) : QWidgetAction(parent)
6{
7  AMFILE= QDir::homePath() + "/.pc-automounttray";   //File to save/load all the devices to be automounted
8  //Set the device info variables
9  if( !newdevice.startsWith(DEVICEDIR) ){ newdevice.prepend(DEVICEDIR); }
10  device = newdevice;
11  devType = newtype;
12  filesystem = newfs;
13  currentUser = user;
14  //Create the layout
15  QGridLayout* layout = new QGridLayout();
16  QHBoxLayout* hlayout = new QHBoxLayout();
17  //Create the gui items
18  devLabel = new QLabel;
19    devLabel->setToolTip(device);
20    devLabel->setText(newlabel);
21   
22  devIcon = new QLabel;
23    devIcon->setToolTip(device);
24  currentSpace = new QProgressBar;
25    currentSpace->setMinimum(0);
26  pushMount = new QPushButton;
27  checkAutomount = new QCheckBox;
28    checkAutomount->setChecked( checkSavedAutoMount() );
29    checkAutomount->setText( tr("Auto-mount this device") );
30  //Add the widgets to the layout
31  layout->addWidget(devIcon,0,0,3,1);   
32  hlayout->addWidget(devLabel);
33  hlayout->addWidget(pushMount);
34  layout->addLayout(hlayout,0,1);
35  layout->addWidget(currentSpace,1,1);
36  layout->addWidget(checkAutomount,2,1);
37  //Set the layout
38  frame = new QWidget();
39  frame->setLayout(layout);
40  this->setDefaultWidget(frame);
41
42  //Connect the signals/slots
43  connect(pushMount,SIGNAL(clicked()), this, SLOT(slotMountClicked()));
44  connect(checkAutomount,SIGNAL(toggled(bool)),this,SLOT(slotAutoMountToggled(bool)));
45
46  //Setup the device Icon based on the type
47  if(devType == "USB"){ devIcon->setPixmap(QPixmap(":icons/usb.png")); }
48  else if(devType == "SATA"){ devIcon->setPixmap(QPixmap(":icons/harddrive.png")); }
49  else if(devType == "SD"){ devIcon->setPixmap(QPixmap(":icons/sdcard.png")); }
50  else if(devType == "CD9660"){ devIcon->setPixmap(QPixmap(":icons/dvd.png")); }
51  else if(devType == "ISO"){devIcon->setPixmap(QPixmap(":icons/dvd.png")); }
52  else if(devType == "SCSI"){devIcon->setPixmap(QPixmap(":icons/harddrive.png")); }
53  //Start the automount procedure if necessary
54  if(checkAutomount->isChecked() || devType=="ISO"){
55    QTimer::singleShot(500,this,SLOT( slotAutoMount() ));
56  }
57  //Update the Item based upon current device status
58  updateItem();
59
60}
61
62MenuItem::~MenuItem(){
63}
64
65/*
66  PUBLIC FUNCTIONS
67*/
68
69void MenuItem::updateItem(){
70  //Update the item visuals, based upon current device status
71  if( isConnected() ){
72    if( isMounted() ){
73      if(mountpoint.isEmpty()){
74        //detect the current mountpoint
75        QString output = pcbsd::Utils::runShellCommandSearch("mount",device);
76        mountpoint = output.section(" on ",1,1).section(" (",0,0).replace(" ","-");
77      }
78      devIcon->setEnabled(TRUE);  //Make the icon full color
79      devIcon->setToolTip(device+"\n"+QString(tr("Mounted at %1")).arg(mountpoint));
80      pushMount->setText(tr("Eject"));
81      pushMount->setIcon(QIcon(":icons/eject.png"));
82      if(devType != "ISO"){ checkAutomount->setVisible(TRUE); }
83      else{ checkAutomount->setVisible(FALSE); }
84    }else{       
85      devIcon->setEnabled(FALSE); //Grey out the icon if not mounted
86      devIcon->setToolTip(device);
87      pushMount->setText(tr("Mount"));
88      pushMount->setIcon(QIcon(":icons/mount.png"));
89      checkAutomount->setVisible(FALSE);
90    }
91  }else{
92    emit itemRemoved(device);
93    return;
94  }
95  //Set visibility and sizes on progressbar
96  updateSizes();
97}
98       
99//Getters
100QString MenuItem::getDeviceName(){
101  return devLabel->text();     
102}
103       
104//Device information
105bool MenuItem::isConnected(){
106  if( QFile::exists(device) ){ return TRUE; }
107  else{ return FALSE; }
108}
109
110bool MenuItem::isMounted(){
111  //Check if device is mounted
112  QStringList chk = pcbsd::Utils::runShellCommand("mount");
113  bool mounted=false;
114  for(int i=0; i<chk.length(); i++){
115    mounted = chk[i].contains(device) || chk[i].contains(devLabel->text()) || chk[i].contains(devLabel->text().replace(" ","-"));
116    if(mounted){ 
117      //Save the mountpoint if it is mounted
118      mountpoint = chk[i].section(" on ",1,10).section("(",0,0).simplified();
119      break; 
120    }
121  }
122  return mounted;
123}
124
125//Cleanup function
126void MenuItem::cleanup(){
127  if( isMounted() ){
128    unmountItem(); //unmount and remove mountpoint
129  }else{
130    //Just check for mountpoint removal
131    if(QFile::exists(mountpoint)){
132      qDebug() << "Removing old mountpoint:" << mountpoint;
133      QString output = pcbsd::Utils::runShellCommand("rmdir "+mountpoint).join(" ");
134      if(!output.isEmpty()){ qDebug() << " -Error:" <<output; }
135    }
136  }
137}
138/*
139  PRIVATE FUNCTIONS
140*/
141void MenuItem::slotMountClicked(){
142  if( isConnected() ){
143    if( !isMounted() ){
144      mountItem();
145    }else{
146      unmountItem(); 
147    }
148  }else{
149    emit itemRemoved(device);     
150  }
151  updateItem();
152}
153
154void MenuItem::slotAutoMount(){
155//Just like slotMountClicked, but will only mount the device if appropriate (no removals);
156  if( isConnected() ){
157    if( !isMounted() ){
158      mountItem();
159    }
160  }else{
161    emit itemRemoved(device);     
162  }
163  updateItem(); 
164}
165
166void MenuItem::slotAutoMountToggled(bool checked){
167  qDebug() << "Auto-mount toggled for:" << device << checked;
168  QString entry = devLabel->text()+":::"+devType+":::"+filesystem;
169  if(checked){
170    //Add this entry to the auto-mount file
171    QString cmd = "echo \""+entry+"\" >> "+AMFILE;
172    system( cmd.toUtf8() );
173  }else{
174    //Remove this entry from the automount file
175    QString tmpFile = AMFILE+".tmp";
176    QString cmd = "cat "+AMFILE+" | grep -v "+entry+" > "+tmpFile+"; mv "+tmpFile+" "+AMFILE;
177    system( cmd.toUtf8() );
178  }
179}
180
181bool MenuItem::checkSavedAutoMount(){
182  if(QFile::exists(AMFILE)){
183    QString cmd = "cat "+AMFILE;
184    QString search = devLabel->text() +" "+ devType +" "+ filesystem;
185    QString chk = pcbsd::Utils::runShellCommandSearch(cmd, search);
186    if( chk.isEmpty() ){ return FALSE; }
187    else{ return TRUE; }
188  }else{
189    return FALSE;
190  }
191}
192
193void MenuItem::mountItem(){
194  //Mount the device
195 
196  //Create the full path to the mountpoint
197  QString deviceName = devLabel->text();
198  QString mntpoint = MOUNTDIR + deviceName.replace(" ","-"); //take into account spaces in the name
199
200  //Create the fileystem specific command for mounting
201  QString fstype;
202  QString fsopts="";
203  if( filesystem == "FAT" ){ fstype = "mount -t msdosfs"; fsopts = "-o large,longnames,-m=644,-M=777"; }
204  else if(filesystem == "NTFS"){ fstype = "ntfs-3g"; }
205  else if(filesystem == "EXT"){ fstype = "mount -t ext2fs"; }
206  else if(filesystem == "CD9660"){ fstype = "mount -t cd9660"; }
207  else if(filesystem == "UFS"){ fstype = "mount -t ufs"; }
208  else if(filesystem == "REISERFS"){ fstype = "mount -t reiserfs"; }
209  else if(filesystem == "XFS"){ fstype = "mount -t xfs"; }
210  else{
211    qDebug() << "Unknown device filesystem:" << device << filesystem << " attempting mount_auto command";
212    fstype = "mount_auto";
213    //QMessageBox::warning(this,tr("Unknown Device Filesystem"),tr("The filesystem on this device is unknown and cannot be mounted at this time") );
214    //return FALSE;
215  }
216  //Make sure the mntpoint is available
217  QDir mpd(mntpoint);
218  if(mpd.exists()){
219    //Remove the existing directory (will work only if it is empty)
220    mpd.cdUp();
221    mpd.rmdir(mntpoint);
222  }
223  //Prepare the commands to run
224  QString cmd1 = "mkdir " + mntpoint;
225  QString cmd2 = fstype + " " +fsopts + " " + device + " " + mntpoint;
226  //cmd2 = "su -m "+currentUser+" -c \""+cmd2+"\""; //add command to run as user
227  QString cmd3 = "chmod 755 " + mntpoint; //to set full user/root access
228  //QString cmd4 = "chown "+currentUser+":"+currentUser+" "+mntpoint; //make the current user the owner
229  qDebug() << "Mounting device" << device << "on" << mntpoint << "("<<filesystem<<")";
230  if(DEBUG_MODE){ qDebug() << " - command:" << cmd2; }
231 
232  bool ok = FALSE;
233  QString result, title;
234  //Run the mounting commands
235  QStringList output = pcbsd::Utils::runShellCommand(cmd1);
236  if( output.join(" ").simplified().isEmpty() ){
237    //directory created, run the next commands
238    //system(cmd4.toUtf8()); //set directory ownershipt before mounting device
239    system(cmd3.toUtf8()); //set directory permissions
240    output = pcbsd::Utils::runShellCommand(cmd2);
241    if( output.join(" ").simplified().isEmpty() ){
242      title = tr("Success");
243      result = QString( tr("%1 mounted at %2") ).arg(deviceName).arg(mntpoint);
244      ok = TRUE;
245    }else{
246      qDebug() << "pc-mounttray: Error mounting device:" << device;
247      qDebug() << " - Error message:" << output;
248      title = QString( tr("Error mounting %1 at %2") ).arg(deviceName).arg(mntpoint);
249      result =  output.join(" ");
250      //Remove the mount point just created
251      pcbsd::Utils::runShellCommand("rmdir "+mntpoint);
252    }
253  }else{
254    qDebug() << "pc-mounttray: Error creating mountpoint:" << mntpoint;
255    qDebug() << " - Error message:" << output;
256    title = QString( tr("Error mounting %1") ).arg(deviceName);
257    result =  QString( tr("Could not create mount point at %1") ).arg(mntpoint);
258  }
259  //Output the proper signals depending upon success
260  if(ok){
261    emit itemMounted(mntpoint);
262    mountpoint = mntpoint;
263  }else{
264    mountpoint.clear();
265  }
266  if( !checkAutomount->isChecked() ){
267    emit newMessage(title, result); //suppress the output message if it was automounted
268  }
269 
270}
271
272void MenuItem::unmountItem(bool force){
273  //Unmount the device
274       
275  //Check to see if the current mountpoint exists or if it is somewhere else
276  if( !QFile::exists(mountpoint) ){
277    if( !isMounted() ){  //double check that it is actually mounted (and fix the mountpoint)
278      //it is not mounted to begin with
279      return;
280    }
281  }
282 
283  //Unmount all the NULLFS mountpoints first (in case it has been mounted into a PBI container)
284  QStringList nullfs = systemCMD("mount").filter(mountpoint).filter("nullfs");
285  bool ok= true;
286  for(int i=0; i<nullfs.length() && ok; i++){
287    QString nfspoint = nullfs[i].section(" on ",1,10).section("(",0,0).simplified();
288    ok = umount(force, nfspoint);
289  }
290  //If successful, also unmount the main mountpoint
291  if(ok){
292    ok = umount(force, mountpoint);
293  }
294  //Make sure there are no spaces in the mounpoint path
295  //QString cmd1 = "umount \"" + mountpoint +"\"";
296  //if(force){ cmd1.replace("umount ","umount -f "); }
297  //QString cmd2 = "rmdir \"" + mountpoint +"\"";
298  //qDebug() << "Unmounting device from" << mountpoint;
299  //Run the commands
300  //QStringList output;
301  QString result, title;
302  /*bool ok = umount(force, mountpoint);
303  output = pcbsd::Utils::runShellCommand(cmd1);
304  if(output.join(" ").simplified().isEmpty()){
305    //unmounting successful, remove the mount point directory
306    if(mountpoint != "/mnt" && mountpoint != "/media"){ //make sure not to remove base directories
307      output = pcbsd::Utils::runShellCommand(cmd2);
308    }
309    if(!output.join(" ").simplified().isEmpty()){
310      qDebug() << "pc-mounttray: Error removing mountpoint:" << mountpoint;
311      qDebug() << " - Error message:" << output;
312    }
313    ok = TRUE;*/
314  if(ok){
315    title = QString( tr("%1 has been successfully unmounted.") ).arg(devLabel->text());
316    if(devType == "ISO"){
317      result = tr("The ISO file has been completely detached from the system.");
318    }else{
319      result = tr("It is now safe to remove the device");
320    }
321  }else{
322    if(!force){
323      if(QMessageBox::Yes == QMessageBox::question(0,tr("Device Busy"),
324                 tr("The device appears to be busy. Would you like to unmount it anyway?")+"\n\n"+tr("NOTE: This is generally not recommended unless you are sure that you don't have any applications using the device."),
325                 QMessageBox::Yes | QMessageBox::No, QMessageBox::No) ){
326        unmountItem(true); //force the unmount recursively
327        return;
328      }
329    }
330    //qDebug() << "pc-mounttray: Error unmounting mountpoint:" << mountpoint;
331    //qDebug() << " - Error message:" << output;
332    title = QString( tr("Error: %1 could not be unmounted") ).arg(devLabel->text());
333    //result = output.join(" ");
334  }
335  //emit the proper signals
336  if(ok){
337    mountpoint.clear();
338    if(devType=="ISO" && device.section("/",-1).startsWith("md") ){
339      //Get the md number
340      QString num = device.section("/md",-1).simplified();
341      //also remove the MD device from the system using "mdconfig"
342      qDebug() << "Detaching Memory Disk:" << num;
343      QString cmd = "mdconfig -d -u "+num;
344      system(cmd.toUtf8());
345    }
346    emit itemUnmounted(device);
347  }
348  emit newMessage(title, result);
349}
350
351void MenuItem::updateSizes(){
352  //this method only works if the device is currently mounted
353  bool ok = FALSE;
354  if(isMounted()){
355    QString cmd = "df \""+mountpoint+"\"";
356    QStringList output = systemCMD(cmd); //make sure we use the one with a 1K blocksize
357    if(output.length() > 1){
358      //parse the output (1K blocks) and save them
359      QString line = output[1].replace("\t"," ");
360      //qDebug() << "df output:" << output << cmd;
361      maxSize = line.section(" ",1,1,QString::SectionSkipEmpty).simplified();
362      currentSize = line.section(" ",2,2,QString::SectionSkipEmpty).simplified();
363      ok=TRUE;
364    }else{
365      maxSize.clear();
366      currentSize.clear();         
367    }
368  }else{
369    maxSize.clear();
370    currentSize.clear();
371  }
372  //Now setup the display progressbar display
373  if(ok){
374    currentSpace->setMaximum( maxSize.toInt() );
375    currentSpace->setValue( currentSize.toInt() );
376    currentSpace->setVisible(TRUE);
377    //display the actual size available in the tooltip
378    QString diskAvailable = getSizeDisplay( maxSize.toInt() - currentSize.toInt() );
379    //qDebug() << "MaxSize:" << maxSize << maxSize.toInt();
380    //qDebug() << "CurrentSize:" << currentSize << currentSize.toInt();
381    //qDebug() << "Disk Available:" << diskAvailable;
382    currentSpace->setToolTip( QString( tr("%1 of disk space available") ).arg(diskAvailable) );
383  }else{
384    currentSpace->setVisible(FALSE);
385  }
386       
387}
388
389QString MenuItem::getSizeDisplay(int kb){
390  //convert from KB to human readable output
391  if( kb > 1048576 ){ //display in GB
392    return QString::number( double(int( (kb*100)/1048576 )/100.0) )+"GB";
393  }else if( kb > 1024 ){ //display in MB
394    return QString::number( double(int( (kb*100)/1024 )/100.0) )+"MB";   
395  }else{ //display in KB
396    return QString::number( kb )+"KB";   
397  }
398       
399}
400
401QStringList MenuItem::systemCMD(QString command){ 
402   QProcess p;
403   QString outstr;
404   //Make sure we use the system environment to properly read system variables, etc.
405   QProcessEnvironment penv = QProcessEnvironment::systemEnvironment();
406   penv.insert("BLOCKSIZE","K"); //make sure we use a 1KB block size
407   p.setProcessEnvironment(penv);
408   //Merge the output channels to retrieve all output possible
409   p.setProcessChannelMode(QProcess::MergedChannels);   
410   p.start(command);
411   while(p.state()==QProcess::Starting || p.state() == QProcess::Running){
412     p.waitForFinished(200);
413     QCoreApplication::processEvents();
414   }
415   QString tmp = p.readAllStandardOutput();
416   outstr.append(tmp);
417   if(outstr.endsWith("\n")){outstr.chop(1);} //remove the newline at the end
418   QStringList out = outstr.split("\n");
419   return out;
420}
421
422bool MenuItem::umount(bool force, QString mntpoint){
423  QString cmd1 = "umount \"" + mntpoint +"\"";
424  if(force){ cmd1.replace("umount ","umount -f "); }
425  QString cmd2 = "rmdir \"" + mountpoint +"\"";
426  qDebug() << "Unmounting device from" << mntpoint;
427  //Run the commands
428  QStringList output;
429  QString result, title;
430  bool ok = FALSE;
431  output = systemCMD(cmd1);
432  if(output.join(" ").simplified().isEmpty()){
433    //unmounting successful, remove the mount point directory
434    if(mountpoint != "/mnt" && mountpoint != "/media"){ //make sure not to remove base directories
435      output = systemCMD(cmd2);
436    }
437    if(!output.join(" ").simplified().isEmpty()){
438      qDebug() << "pc-mounttray: Error removing mountpoint:" << mountpoint;
439      qDebug() << " - Error message:" << output;
440    }
441    ok = TRUE;
442  }else{
443    qDebug() << "pc-mounttray: Error unmounting mountpoint:" << mountpoint;
444    qDebug() << " - Error message:" << output;
445  }
446  return ok;
447}
448
Note: See TracBrowser for help on using the repository browser.