source: src-qt4/pc-mounttray/mountTray.cpp @ b3ee7b3

9.2-releasereleng/10.0releng/10.0.1releng/10.0.2
Last change on this file since b3ee7b3 was b3ee7b3, checked in by Ken <ken@…>, 10 months ago

Clean up the mount tray FM opening function - it looks like the problems with xdg-open are related to missing environment variables with the "sudo" start of the app

  • Property mode set to 100644
File size: 16.5 KB
Line 
1
2/* Qt */
3#include <QDebug>
4#include <QFile>
5#include <QDir>
6#include <QImage>
7#include <QMenu>
8#include <QTranslator>
9
10#include "mountTray.h"
11#include <pcbsd-hardware.h>
12#include <pcbsd-utils.h>
13
14MountTray::~MountTray(){
15}
16
17void MountTray::programInit()
18{
19  DCheck = new DevCheck(); //initialize class for checking devices
20  qDebug() << "pc-mounttray: starting up";
21  MTINIT=true; //set the flag that the mount tray is initializing;
22  getInitialUsername(); //try to detect the non-root user who is running the program with root permissions
23  loadSavedSettings();
24 
25  trayIcon = new QSystemTrayIcon(this);
26  trayIconMenu = new QMenu();
27  //Generate the system menu options (these don't change)
28  sysMenu = new QMenu( tr("More Options") );
29    sysMenu->setIcon( QIcon(":icons/config.png") );
30    //Add the additional options
31    sysMenu->addAction( QIcon(":icons/folder.png"), tr("Open Media Directory"), this, SLOT(slotOpenMediaDir()) );
32    sysMenu->addAction( QIcon(":icons/harddrive.png"), tr("View Disk Usage"),this,SLOT(slotOpenFSDialog()) );
33    sysMenu->addAction( QIcon(":icons/refresh.png"),tr("Rescan Devices"), this, SLOT(slotRescan()) );
34    //Add the setting dialog option seperately
35    sysMenu->addSeparator();
36    sysMenu->addAction( QIcon(":icons/dvd.png"), tr("Load ISO File"), this, SLOT(slotOpenISO()) );
37    sysMenu->addAction( QIcon(":icons/config.png"), tr("Change Settings"), this, SLOT(slotOpenSettings()) );
38    //Add the Close button seperately
39    sysMenu->addSeparator();
40    sysMenu->addAction( QIcon(":icons/application-exit.png"), tr("Close Tray"), this, SLOT(closeTray()) );
41 
42  // Tie the left-click signal to open the context menu
43  connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(slotTrayActivated(QSystemTrayIcon::ActivationReason)) );
44  //Connect the message clicked slot
45  connect(trayIcon,SIGNAL(messageClicked()),this,SLOT(slotPopupClicked()) );
46  //Set the default Tray Icon (will change once tray menus are set)
47  trayIcon->setIcon(QIcon(":icons/CDdevices-inactive.png"));
48  trayIcon->show();
49
50  //Startup the devd watching process
51  qDebug() << "-Starting up the DEVD watcher";
52  devdTimer = new QTimer();
53  devdTimer->setSingleShot(TRUE);
54  connect(devdTimer,SIGNAL(timeout()),this,SLOT(slotDevChanges()));
55  startupDevdProc();
56 
57  //Do an initial scan of the devices with dmesg
58  qDebug() << "-Performing initial device scan";
59  scanInitialDevices();
60 
61  //Start up the filesystem watcher
62  diskWatcher = new FSWatcher();
63  connect(diskWatcher,SIGNAL(FSWarning(QString,QString)),this,SLOT(slotDisplayWarning(QString,QString)));
64  if(useDiskWatcher){ 
65    qDebug() << "-Starting up the disk space alert system";
66    diskWatcher->start(diskTimerMaxMS); 
67  }
68 
69  //Update the tray menu and icons
70  updateMenu();
71
72  qDebug() << "-Program now ready for use";
73  MTINIT=false;
74}
75
76void MountTray::updateMenu(){
77  //Clear the menu
78  trayIconMenu->clear();
79  trayIconMenu->disconnect();
80  numAvail = 0;
81  numMount = 0;
82  //Iterate through all the devices and add them as necessary
83  for(int i=0; i<deviceList.length(); i++){
84    if(deviceList[i]->isConnected()) { 
85      trayIconMenu->addAction(deviceList[i]);
86      numAvail++;
87      if(deviceList[i]->isMounted() ){ numMount++; }
88      deviceList[i]->updateItem(); //refresh the item now as well
89    }else{
90      //Remove any devices that are not connected
91      deviceList.removeAt(i);
92      i--; //roll back one to catch the new index values for the list
93    }
94  }
95  //Separate the extra options at the end
96  trayIconMenu->addSeparator();
97  trayIconMenu->addMenu(sysMenu);
98  //Apply the menu to the Tray
99  trayIcon->setContextMenu(trayIconMenu);
100
101  //Update the main icon based upon whether devices have been found
102  if(numAvail==0){
103    trayIcon->setIcon( QIcon(":icons/CDdevices-inactive.png") );
104  }else{
105    if(numMount==0){
106      trayIcon->setIcon( QIcon(":icons/CDdevices.png") );
107    }else{
108      trayIcon->setIcon( QIcon(":icons/CDdevices.png") );
109    }
110  }
111}
112
113void MountTray::scanInitialDevices(){
114  slotDevChanges(FALSE);
115  return;
116}
117
118int MountTray::findDeviceInList(QString newDev){
119  for(int i=0; i<deviceList.length(); i++){
120    if( deviceList[i]->device == newDev ){ return i; }
121  }
122  return -1;
123}
124
125bool MountTray::addDevice(QString dev, QString label, QString type, QString filesys){
126  if(!dev.startsWith(DEVICEDIR)){ dev.prepend(DEVICEDIR); }
127 
128  //Check if the device is already in the list
129  int tot=0;
130  for(int i=0; i<deviceList.length(); i++){
131    if( deviceList[i]->device == dev ){ return false; } //already exists, do nothing
132    if( deviceList[i]->getDeviceName().startsWith(label) ){ tot++; }
133  }
134  //See if the label is unique as well, otherwise add a number to the end to make it unique
135  if(tot > 0 && !label.isEmpty()){ label.append("-"+QString::number(tot)); }
136 
137  qDebug() << "Valid Device Connection:" << dev << type << label << filesys;
138  //Create the menu item (will automount if necessary)
139  MenuItem *tmp = new MenuItem(this, dev, label, type, filesys);
140  //connect the signals/slots
141  connect(tmp, SIGNAL(itemMounted(QString)), this, SLOT(openMediaDir(QString)) );
142  connect(tmp, SIGNAL(newMessage(QString,QString)), this, SLOT(slotDisplayPopup(QString,QString)) );
143  connect(tmp, SIGNAL(itemRemoved(QString)), this, SLOT(removeDevice(QString)) );
144  deviceList << tmp;
145  //Update the menu
146  updateMenu();
147  return true;
148}
149
150void MountTray::removeDevice(QString dev){
151  if(!dev.startsWith(DEVICEDIR)){ dev.prepend(DEVICEDIR); }
152 
153  //Find the device in the list
154  int index = findDeviceInList(dev);
155  if( index == -1 ){ return; } //does not exist, do nothing
156  //Remove the menu entry from the list
157  deviceList[index]->cleanup(); //make sure it is unmounted with mountpoint removed
158  deviceList.removeAt(index);
159  //Update the menu
160  updateMenu();
161  qDebug() << "Valid Device Removal:" <<  dev;
162}
163
164void MountTray::slotTrayActivated(QSystemTrayIcon::ActivationReason reason) {
165   if(reason == QSystemTrayIcon::Trigger) {
166     //Make sure all the items are updated
167     for(int i=0; i<deviceList.length(); i++){
168        deviceList[i]->updateItem();
169     }
170     trayIcon->contextMenu()->popup(QCursor::pos());
171   }
172}
173
174
175void MountTray::startupDevdProc(){
176  devdProc = new QLocalSocket(this);
177  devdProc->connectToServer("/var/run/devd.pipe",QIODevice::ReadOnly | QIODevice::Text);
178  if( devdProc->waitForConnected(3000) ){ //Max wait of 3 sec
179    connect(devdProc, SIGNAL(readyRead()), this, SLOT(newDevdMessage()) );
180  }else{
181    qDebug() << " - Could not startup the devd watching process";
182  }
183}
184
185void MountTray::newDevdMessage(){       
186  devdTimer->start(1500); //wait 1.5 seconds before checking for device changes
187  return;
188}
189
190void MountTray::slotDevChanges(bool showPopup){
191  //This function actually checks the system device list for changes
192  //  and updates the available devices appropriately
193 
194  if(DEBUG_MODE){ qDebug() << "Checking for Device Changes:"; }
195  //Get the current list of devices
196  QStringList nsd = DCheck->devChildren("");
197  //Remove all the currently managed devices
198  qDebug() << "Rescanning Device List";
199  for(int i=0; i<deviceList.length(); i++){
200    QString dev = deviceList[i]->device.section("/",-1);
201    if(DEBUG_MODE){ qDebug() << " - Check device:" << dev; }
202    int ni = nsd.indexOf(dev);
203    if(ni == -1){
204      //Device Removed
205      if(DEBUG_MODE){ qDebug() << " - Device no longer connected:" << dev; }
206      removeDevice(dev);
207      i--;
208    }else{
209      //Probe the device for validity if not currently mounted
210      if( !deviceList[i]->isMounted() ){
211        QString ja, jb, jc, jd; //junk variables
212        if( !DCheck->devInfo(dev,&ja,&jb,&jc,&jd) ){
213          if(DEBUG_MODE){ qDebug() << " - Device no longer valid:" << dev; }
214          //no longer valid device
215          removeDevice(dev);
216          i--;
217        }
218      }
219      nsd.removeAt(ni);
220    }
221  }
222  //Now Iterate through all available devices and probe them for validity
223  // (This should catch devices that do not "announce" their presence by creating a new device node)
224  for(int i=0; i<nsd.length(); i++){
225    //Check if it is a good device
226    QString dlabel, dtype, dfs, dsize; //additional output info
227    bool good = DCheck->devInfo(nsd[i],&dtype,&dlabel,&dfs,&dsize);
228    if(good){
229      //Now create a new entry for this device
230      bool added = addDevice(nsd[i],dlabel,dtype,dfs); 
231      //Show a message bubble
232      if(showPopup && added){ //make sure this is not shown for previously added devices
233        QString title = tr("New Device");
234        QString message = QString( tr("%1 can now be accessed")).arg(dlabel);
235        slotDisplayPopup(title, message, nsd[i]);
236      }
237    }
238  }
239 
240  //Run the disk space check if appropriate
241  if(useDiskWatcher && useDiskTimerDevd && showPopup){ diskWatcher->checkFS(); }
242}
243
244void MountTray::closeTray(){
245  qDebug() << "pc-mounttray: closing down";
246  //Kill the devd watching process
247  qDebug() << " -Shutting down DEVD watcher";
248  devdProc->disconnectFromServer();
249  qDebug() << " -Unmounting managed devices and mount points";
250  for(int i=0; i<deviceList.length(); i++){
251    deviceList[i]->cleanup();
252  }
253  //Close down the application
254  exit(0);
255}
256
257void MountTray::getInitialUsername(){
258  //Get the original user who started the tray app
259  QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
260  //qDebug() << "System Environment:" << env.toStringList();
261  QString username = env.value( "LOGNAME" );
262  //qDebug() << "First attempt user detected:" << username;
263  if(username=="root"){
264    username = env.value( "SUDO_USER" ); //try this environment variable instead
265    //qDebug() << "Second attempt user detected:" << username;
266  }
267  if(username=="root" || username.isEmpty() ){
268   //attempt another method of determining the username
269   QStringList uList = pcbsd::Utils::runShellCommand("who");
270   if( uList.length() > 0 ){ 
271     username = uList[0].section(" ",0,0,QString::SectionSkipEmpty);
272     for(int i=0; i<uList.length(); i++){
273       if( !uList[i].section(" ",0,0,QString::SectionSkipEmpty).contains(username) ){
274         username = "root"; //too many users logged in; this method will not work
275         break;
276       }
277     }
278     //qDebug() << "Third attempt user detected:" << username;
279   }
280  }
281  if(username=="root"){
282    QMessageBox::warning(this,tr("User Detection Error"),tr("Unable to determine the non-root user who started the application \nCan not open the file manager with root permissions") );
283    return;
284  }
285  USERNAME=username.simplified(); //set the global variable
286  if(DEBUG_MODE){ qDebug() << "-User detected:" << USERNAME; }
287}
288
289void MountTray::slotOpenMediaDir(){
290  openMediaDir(MOUNTDIR);
291}
292
293void MountTray::openMediaDir(QString dir){
294  if(MTINIT){ return; } //don't open the FM during program initialization
295  //Open the default file-manager to the directory listed
296  if(dir.isEmpty()){ dir = MOUNTDIR; }
297  if(!dir.endsWith("/")){ dir.append("/"); } //make sure the filemanager knows it is a directory
298  //Make sure we can setup user permissions
299  if(USERNAME=="root"){
300    qDebug() << "Cannot open filemanager with root permissions";
301    return;
302  }
303  //Open the default file manager to the given directory as that user
304  qDebug() << "Opening the media directory with user permissions";
305  QString cmd = "su -m "+USERNAME+" -c \"xdg-open \'"+dir+"\' \"";
306  if(DEBUG_MODE){ qDebug() << " -cmd:" << cmd ; }
307  cmd.prepend("("); cmd.append(") &");
308  system( cmd.toUtf8() );
309}
310
311void MountTray::slotRescan(){
312  //Display a notification
313  qDebug() << "Re-scanning devices:";
314  slotDisplayPopup(tr("Please Wait"),tr("Rescanning devices attached to the system"));
315  //Rescan the device list for new devices
316  scanInitialDevices();
317  //Check that all the existing devices still exist
318  for(int i=0; i<deviceList.length(); i++){
319    deviceList[i]->updateItem();
320  }
321  //Run the disk check if appropriate
322  if(useDiskWatcher){ diskWatcher->checkFS(); }
323}
324
325void MountTray::slotOpenFSDialog(){
326  //Open up the Filesystem disk space monitoring dialog
327  diskDisplay = new FSDialog();
328  diskDisplay->show();
329}
330
331void MountTray::slotOpenSettings(){
332  //Stop the refresh timer on the watcher
333  diskWatcher->stop();
334  //Open up the settings window and apply changes as necessary
335  SettingsDialog *sdlg = new SettingsDialog();
336  sdlg->useDiskWatcher = useDiskWatcher;
337  sdlg->useDiskAutoTimer = useDiskTimerDevd;
338  sdlg->diskRefreshMS = diskTimerMaxMS;
339  sdlg->showDialog();
340  //Now parse the output and save if necessary
341  if(sdlg->SettingsChanged){
342    useDiskWatcher = sdlg->useDiskWatcher;
343    useDiskTimerDevd = sdlg->useDiskAutoTimer;
344    diskTimerMaxMS = sdlg->diskRefreshMS;
345    qDebug() << "INFO: Saving updated settings to file";
346    saveCurrentSettings(); //update the saved settings
347  }
348  //Now restart the disk watcher if enabled
349  if(useDiskWatcher){ diskWatcher->start(diskTimerMaxMS); }
350}
351
352void MountTray::slotOpenISO(){
353  //prompt for the user to select a file
354  QString file = QFileDialog::getOpenFileName( this, tr("Select ISO File"), QDir::homePath(), tr("ISO Files (*.iso)") );
355  if(file.isEmpty()){ return; } //cancelled
356  //check for available device node number /dev/md<number>
357  int num = 1;
358  while( QFile::exists("/dev/md"+QString::number(num)) ){ num++; }
359  //add it to the device tree (will automatically get picked up by the device detection method)
360  QString cmd = "mdconfig -a -f "+file+" -u "+QString::number(num);
361  system(cmd.toUtf8());
362}
363
364void MountTray::slotSingleInstance()
365{
366  trayIcon->show();
367}
368
369void MountTray::slotDisplayPopup(QString title, QString msg, QString device){
370  popupSave = device; //so we know what to do when it is clicked
371  //Display a popup bubble with the given message for 3 seconds
372  trayIcon->contextMenu()->hide(); //close the menu list
373  trayIcon->showMessage(title, msg , QSystemTrayIcon::NoIcon,3000 );
374}
375
376void MountTray::slotDisplayWarning(QString title, QString msg){
377  popupSave="FSCHECK";
378  //Display a popup bubble with the given message for 5 seconds
379  trayIcon->contextMenu()->hide(); //close the menu list
380  trayIcon->showMessage(title, msg , QSystemTrayIcon::Warning,5000 );
381}
382
383void MountTray::slotPopupClicked(){
384  //Check the saved variable for what to do with this popup
385  if(popupSave == "FSCHECK"){
386    //Open up the filesystem disk space UI
387    slotOpenFSDialog();
388  }else if(!popupSave.isEmpty()){
389    //Check if it is a currently valid device
390    if(!popupSave.startsWith(DEVICEDIR)){ popupSave.prepend(DEVICEDIR); }
391    for(int i=0; i<deviceList.length(); i++){
392      if( deviceList[i]->device == popupSave){
393        //See if the device is mounted
394        if(deviceList[i]->isMounted()){
395          //Open up the mountpoint directory
396          openMediaDir(deviceList[i]->mountpoint);
397        }else{
398          //Mount the device
399          deviceList[i]->mountItem();
400        }
401        break;
402      }
403    }
404  }
405
406}
407
408void MountTray::loadSavedSettings(){
409  //The saved settings file
410  QString filename = QDir::homePath()+"/.mounttray.settings";
411  //Set the defaults
412  useDiskWatcher=TRUE; useDiskTimerDevd=TRUE;
413  diskTimerMaxMS=3600000; //1 hour refresh timer
414  //Now load the file
415  QFile file(filename);
416  if(file.exists()){
417    if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){ 
418      qDebug() << "-Could not open settings file: using defaults";
419      return; 
420    }
421    QTextStream in(&file);
422    while(!in.atEnd()){
423      QString line = in.readLine();
424      if(!line.startsWith("#")){ //skip comment lines
425        QString var = line.section(")",0,0,QString::SectionSkipEmpty).simplified();
426        QString val = line.section(")",1,30,QString::SectionSkipEmpty).simplified();
427        if(var=="UseDiskSpaceMonitoring"){ 
428          if(val.toLower() == "true"){ useDiskWatcher = TRUE;}
429          else{ useDiskWatcher = FALSE; }
430        }else if(var=="UseDiskSpaceDevdTiming"){
431          if(val.toLower() == "true"){ useDiskTimerDevd = TRUE;}
432          else{ useDiskTimerDevd = FALSE; }     
433        }else if(var=="DiskSpaceTimingMaxMilliseconds"){
434          diskTimerMaxMS = val.toInt(); 
435        }
436      }
437    }
438    file.close();
439  }else{
440    qDebug() << "-Creating new settings file with defaults";
441    saveCurrentSettings();
442  }
443}
444
445void MountTray::saveCurrentSettings(){
446  //The saved settings file
447  QString filename = QDir::homePath()+"/.mounttray.settings";
448  //Now write the current values to the file
449  QFile file(filename);
450  if(!file.open(QIODevice::WriteOnly | QIODevice::Text)){
451    qDebug() << "ERROR: Could not open file to save settings:"<<filename;
452    return;
453  }
454  QTextStream out(&file);
455  out << "#pc-mounttray saved settings file\n";
456  out << "# DO NOT EDIT: Use the settings dialog in the application instead!!\n";
457  //Save the settings
458  out << "UseDiskSpaceMonitoring)";
459  if(useDiskWatcher){ out << "true\n";}
460  else{ out << "false\n"; }
461  out << "UseDiskSpaceDevdTiming)";
462  if(useDiskTimerDevd){ out << "true\n";}
463  else{ out << "false\n"; }
464  out << "DiskSpaceTimingMaxMilliseconds)"+QString::number(diskTimerMaxMS)+"\n";
465  //Now close the file
466  file.close();
467}
Note: See TracBrowser for help on using the repository browser.