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

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

When mounting FAT filesystems, also use the -L=<locale-code> flag for international users with special characters that need proper conversion.

  • 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 = QString("-o large,longnames,-m=644,-M=777,-L=")+QString(getenv("LANG")); }
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.