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

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

Make sure we don't run the user ownership on the mountpoint. That code was experimental and does not work properly yet.

  • Property mode set to 100644
File size: 14.3 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  //Make sure there are no spaces in the mounpoint path
283  QString cmd1 = "umount \"" + mountpoint +"\"";
284  if(force){ cmd1.replace("umount ","umount -f "); }
285  QString cmd2 = "rmdir \"" + mountpoint +"\"";
286  qDebug() << "Unmounting device from" << mountpoint;
287  //Run the commands
288  QStringList output;
289  QString result, title;
290  bool ok = FALSE;
291  output = pcbsd::Utils::runShellCommand(cmd1);
292  if(output.join(" ").simplified().isEmpty()){
293    //unmounting successful, remove the mount point directory
294    if(mountpoint != "/mnt" && mountpoint != "/media"){ //make sure not to remove base directories
295      output = pcbsd::Utils::runShellCommand(cmd2);
296    }
297    if(!output.join(" ").simplified().isEmpty()){
298      qDebug() << "pc-mounttray: Error removing mountpoint:" << mountpoint;
299      qDebug() << " - Error message:" << output;
300    }
301    ok = TRUE;
302    title = QString( tr("%1 has been successfully unmounted.") ).arg(devLabel->text());
303    if(devType == "ISO"){
304      result = tr("The ISO file has been completely detached from the system.");
305    }else{
306      result = tr("It is now safe to remove the device");
307    }
308  }else{
309    if(!force){
310      if(QMessageBox::Yes == QMessageBox::question(0,tr("Device Busy"),
311                 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."),
312                 QMessageBox::Yes | QMessageBox::No, QMessageBox::No) ){
313        unmountItem(true); //force the unmount recursively
314        return;
315      }
316    }
317    qDebug() << "pc-mounttray: Error unmounting mountpoint:" << mountpoint;
318    qDebug() << " - Error message:" << output;
319    title = QString( tr("Error: %1 could not be unmounted") ).arg(devLabel->text());
320    result = output.join(" ");
321  }
322  //emit the proper signals
323  if(ok){
324    mountpoint.clear();
325    if(devType=="ISO" && device.section("/",-1).startsWith("md") ){
326      //Get the md number
327      QString num = device.section("/md",-1).simplified();
328      //also remove the MD device from the system using "mdconfig"
329      qDebug() << "Detaching Memory Disk:" << num;
330      QString cmd = "mdconfig -d -u "+num;
331      system(cmd.toUtf8());
332    }
333    emit itemUnmounted(device);
334  }
335  emit newMessage(title, result);
336}
337
338void MenuItem::updateSizes(){
339  //this method only works if the device is currently mounted
340  bool ok = FALSE;
341  if(isMounted()){
342    QString cmd = "df \""+mountpoint+"\"";
343    QStringList output = systemCMD(cmd); //make sure we use the one with a 1K blocksize
344    if(output.length() > 1){
345      //parse the output (1K blocks) and save them
346      QString line = output[1].replace("\t"," ");
347      //qDebug() << "df output:" << output << cmd;
348      maxSize = line.section(" ",1,1,QString::SectionSkipEmpty).simplified();
349      currentSize = line.section(" ",2,2,QString::SectionSkipEmpty).simplified();
350      ok=TRUE;
351    }else{
352      maxSize.clear();
353      currentSize.clear();         
354    }
355  }else{
356    maxSize.clear();
357    currentSize.clear();
358  }
359  //Now setup the display progressbar display
360  if(ok){
361    currentSpace->setMaximum( maxSize.toInt() );
362    currentSpace->setValue( currentSize.toInt() );
363    currentSpace->setVisible(TRUE);
364    //display the actual size available in the tooltip
365    QString diskAvailable = getSizeDisplay( maxSize.toInt() - currentSize.toInt() );
366    //qDebug() << "MaxSize:" << maxSize << maxSize.toInt();
367    //qDebug() << "CurrentSize:" << currentSize << currentSize.toInt();
368    //qDebug() << "Disk Available:" << diskAvailable;
369    currentSpace->setToolTip( QString( tr("%1 of disk space available") ).arg(diskAvailable) );
370  }else{
371    currentSpace->setVisible(FALSE);
372  }
373       
374}
375
376QString MenuItem::getSizeDisplay(int kb){
377  //convert from KB to human readable output
378  if( kb > 1048576 ){ //display in GB
379    return QString::number( double(int( (kb*100)/1048576 )/100.0) )+"GB";
380  }else if( kb > 1024 ){ //display in MB
381    return QString::number( double(int( (kb*100)/1024 )/100.0) )+"MB";   
382  }else{ //display in KB
383    return QString::number( kb )+"KB";   
384  }
385       
386}
387
388QStringList MenuItem::systemCMD(QString command){ 
389   QProcess p;
390   QString outstr;
391   //Make sure we use the system environment to properly read system variables, etc.
392   QProcessEnvironment penv = QProcessEnvironment::systemEnvironment();
393   penv.insert("BLOCKSIZE","K"); //make sure we use a 1KB block size
394   p.setProcessEnvironment(penv);
395   //Merge the output channels to retrieve all output possible
396   p.setProcessChannelMode(QProcess::MergedChannels);   
397   p.start(command);
398   while(p.state()==QProcess::Starting || p.state() == QProcess::Running){
399     p.waitForFinished(200);
400     QCoreApplication::processEvents();
401   }
402   QString tmp = p.readAllStandardOutput();
403   outstr.append(tmp);
404   if(outstr.endsWith("\n")){outstr.chop(1);} //remove the newline at the end
405   QStringList out = outstr.split("\n");
406   return out;
407}
Note: See TracBrowser for help on using the repository browser.