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

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

Clean up the device naming scheme in the mount tray:
1) Do not use glabel for NTFS devices (when mounted through fuse glabel is not consistent in giving the label)
2) Use device's camcontrol identifyier for SATA devices as well as USB devices. This should provide a more specific name than "SATA-Device".
3) For a camcontrol name, append "-" followed by the partition number (so ada0s1a will have a "-1a" appended to the end. Again, this should help devices have unique names.

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