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

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

Make sure that linsysfs is skipped by the mount tray disk watcher, and also move the nullfs skip to the actual disk watcher as well.

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