disklist.cpp 18.9 KB
Newer Older
1 2 3 4
/*
 * disklist.cpp
 *
 * Copyright (c) 1999 Michael Kropfberger <michael.kropfberger@gmx.net>
5
 *               2009 Dario Andres Rodriguez <andresbajotierra@gmail.com>
6 7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
Script Kiddy's avatar
Script Kiddy committed
20
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21
 */
Dirk Mueller's avatar
Dirk Mueller committed
22

23
#include "disklist.h"
24

25
#include "kdf_debug.h"
26
#include "kdfutil.h"
27

Andreas Pakulat's avatar
Andreas Pakulat committed
28
#include <kconfiggroup.h>
29
#include <kdefakes.h>
30
#include <kprocess.h>
31
#include <klocale.h>
32

33 34 35 36 37 38 39
#include <QTextStream>
#include <QFile>
#include <QRegExp>

#include <math.h>
#include <stdlib.h>

40 41
static const QLatin1Char Blank = QLatin1Char( ' ' );
static const QLatin1Char Delimiter = QLatin1Char( '#' );
42 43 44 45

/***************************************************************************
  * constructor
**/
Dirk Mueller's avatar
Dirk Mueller committed
46
DiskList::DiskList(QObject *parent)
47
        : QObject(parent), dfProc(new KProcess(this))
Dirk Mueller's avatar
Dirk Mueller committed
48
{
49
    qCDebug(KDF);
50

51
    updatesDisabled = false;
52

53 54
    if (No_FS_Type)
    {
55
        qCDebug(KDF) << "df gives no FS_TYPE";
56
    }
Dirk Mueller's avatar
Dirk Mueller committed
57

58
    disks = new Disks();
59

60 61
    // BackgroundProcesses ****************************************
    dfProc->setOutputChannelMode(KProcess::MergedChannels);
Laurent Montel's avatar
Laurent Montel committed
62 63
    connect(dfProc,SIGNAL(finished(int,QProcess::ExitStatus)),
            this, SLOT(dfDone()) );
64

65
    readingDFStdErrOut=false;
Yoann Laissus's avatar
Yoann Laissus committed
66
    config = KSharedConfig::openConfig();
67
    loadSettings();
Dirk Mueller's avatar
Dirk Mueller committed
68
}
69 70 71 72 73 74 75


/***************************************************************************
  * destructor
**/
DiskList::~DiskList()
{
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
    dfProc->disconnect();
    if( dfProc->state() == QProcess::Running )
    {
        dfProc->terminate();
        dfProc->waitForFinished();
    }
    delete dfProc;
    //We have to delete the diskentries manually, otherwise they get leaked (?)
    // (they aren't released on delete disks )
    DisksIterator itr = disksIteratorBegin();
    DisksIterator end = disksIteratorEnd();
    while( itr != end )
    {
        DisksIterator prev = itr;
        ++itr;

        DiskEntry * disk = *prev;
        disks->erase( prev );
        delete disk;
95
    }
96
    delete disks;
Dirk Mueller's avatar
Dirk Mueller committed
97
}
98

Maks Orlovich's avatar
 
Maks Orlovich committed
99 100 101 102 103 104 105
/**
Updated need to be disabled sometimes to avoid pulling the DiskEntry out from the popupmenu handler
*/
void DiskList::setUpdatesDisabled(bool disable)
{
    updatesDisabled = disable;
}
106 107 108 109 110 111

/***************************************************************************
  * saves the KConfig for special mount/umount scripts
**/
void DiskList::applySettings()
{
112
    qCDebug(KDF);
113 114 115 116 117 118 119 120 121

    KConfigGroup group(config, "DiskList");
    QString key;

    DisksConstIterator itr = disksConstIteratorBegin();
    DisksConstIterator end = disksConstIteratorEnd();
    for (; itr != end; ++itr)
    {
        DiskEntry * disk = *itr;
122

123 124 125 126 127 128 129 130 131 132
        key = QLatin1String("Mount") + Separator + disk->deviceName() + Separator + disk->mountPoint();
        group.writePathEntry(key,disk->mountCommand());

        key = QLatin1String("Umount") + Separator + disk->deviceName() + Separator + disk->mountPoint();
        group.writePathEntry(key,disk->umountCommand());

        key = QLatin1String("Icon") + Separator + disk->deviceName() + Separator + disk->mountPoint();
        group.writePathEntry(key,disk->realIconName());
    }
    group.sync();
133 134 135 136 137 138 139 140
}


/***************************************************************************
  * reads the KConfig for special mount/umount scripts
**/
void DiskList::loadSettings()
{
141
    qCDebug(KDF);
142 143 144

    const KConfigGroup group(config, "DiskList");
    QString key;
145

146 147 148 149 150
    DisksConstIterator itr = disksConstIteratorBegin();
    DisksConstIterator end = disksConstIteratorEnd();
    for (; itr != end; ++itr)
    {
        DiskEntry * disk = *itr;
151

152 153 154 155 156 157 158 159 160 161 162
        key = QLatin1String("Mount") + Separator + disk->deviceName() + Separator + disk->mountPoint();
        disk->setMountCommand(group.readPathEntry(key, QString()));

        key = QLatin1String("Umount") + Separator + disk->deviceName() + Separator + disk->mountPoint();
        disk->setUmountCommand(group.readPathEntry(key, QString()));

        key = QLatin1String("Icon") + Separator + disk->deviceName() + Separator + disk->mountPoint();
        QString icon=group.readPathEntry(key, QString());
        if (!icon.isEmpty())
            disk->setIconName(icon);
    }
163 164
}

George Staikos's avatar
George Staikos committed
165 166

static QString expandEscapes(const QString& s) {
167 168 169
    QString rc;
    for (int i = 0; i < s.length(); i++)
    {
170
        if (s[i] == QLatin1Char( '\\' ))
171
        {
George Staikos's avatar
George Staikos committed
172
            i++;
173
            QChar str=s.at(i);
174 175 176
            if( str == QLatin1Char( '\\' ))
                rc += QLatin1Char( '\\' );
            else if( str == QLatin1Char( '0' ))
177
            {
178
                rc += QLatin1Char( s.mid(i,3).toULongLong(0, 8) );
179 180 181 182 183 184
                i += 2;
            }
            else
            {
                // give up and not process anything else because I'm too lazy
                // to implement other escapes
185
                rc += QLatin1Char( '\\' );
186 187 188 189 190
                rc += s[i];
            }
        }
        else
        {
George Staikos's avatar
George Staikos committed
191 192 193
            rc += s[i];
        }
    }
194
    return rc;
George Staikos's avatar
George Staikos committed
195 196
}

197 198 199 200 201
/***************************************************************************
  * tries to figure out the possibly mounted fs
**/
int DiskList::readFSTAB()
{
202
    qCDebug(KDF);
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219

    if (readingDFStdErrOut || (dfProc->state() != QProcess::NotRunning))
        return -1;

    QFile f(FSTAB);
    if ( f.open(QIODevice::ReadOnly) )
    {
        QTextStream t (&f);
        QString s;
        DiskEntry *disk;

        //disks->clear(); // ############

        while (! t.atEnd())
        {
            s=t.readLine();
            s=s.simplified();
220 221 222

	    if ( (!s.isEmpty() ) && (s.indexOf(Delimiter)!=0) )
	    {
223
                // not empty or commented out by '#'
224
                qCDebug(KDF) << "GOT: [" << s << "]";
225 226
                disk = new DiskEntry();
                disk->setMounted(false);
227
		QFile path(QLatin1String( "/dev/disk/by-uuid/" ));
228 229
		// We need to remove UUID=
		// TODO: Fix for other OS if using UUID and not using /dev/disk/by-uuid/
230
		if ( s.contains(QLatin1String( "UUID=" )) )
231 232 233
		{
			if (path.exists())
			{
234
				QRegExp uuid( QLatin1String( "UUID=(\\S+)(\\s+)" ));
235
				QString extracted ;
236
				if (uuid.indexIn(s) != -1)
237 238 239
				{
					extracted = uuid.cap(1);
				}
240

241 242 243 244
				if (! extracted.isEmpty() )
				{
					QString device = path.fileName() + extracted;
					QFile file(device);
245

246 247 248 249 250 251 252
					if ( file.exists() )
					{
						QString filesym = file.symLinkTarget();
						disk->setDeviceName(filesym);
					}
					else
					{
253
						qCDebug(KDF) << "The device does not seems to exist";
254 255 256 257 258
						continue;
					}
				}
				else
				{
259
					qCDebug(KDF) << "Invalid UUID";
260 261 262 263 264
					continue;
				}
			}
			else
			{
265
				qCDebug(KDF) << "UUID OK but there is no /dev/disk/by-uuid/";
266 267 268 269 270 271 272
				continue;
			}
		}
		else
		{
			disk->setDeviceName(expandEscapes(s.left(s.indexOf(Blank))));
		}
273

274
		s=s.remove(0,s.indexOf(Blank)+1 );
275
#ifdef _OS_SOLARIS_
276 277
                //device to fsck
                s=s.remove(0,s.indexOf(Blank)+1 );
Dirk Mueller's avatar
Dirk Mueller committed
278
#endif
279 280 281 282
		disk->setMountPoint(expandEscapes(s.left(s.indexOf(Blank))));
		s=s.remove(0,s.indexOf(Blank)+1 );
		disk->setFsType(s.left(s.indexOf(Blank)) );
		s=s.remove(0,s.indexOf(Blank)+1 );
283 284 285
                disk->setMountOptions(s.left(s.indexOf(Blank)) );
                s=s.remove(0,s.indexOf(Blank)+1 );

286 287 288
                if ( (disk->deviceName() != QLatin1String( "none" ))
                        && (disk->fsType() != QLatin1String( "swap" ))
                        && (disk->fsType() != QLatin1String( "sysfs" ))
289 290 291 292
                        && (disk->fsType() != QLatin1String( "rootfs" ))
                        && (disk->fsType() != QLatin1String( "tmpfs" ))
                        && (disk->fsType() != QLatin1String( "debugfs" ))
                        && (disk->fsType() != QLatin1String( "devtmpfs" ))
293 294 295
                        && (disk->mountPoint() != QLatin1String( "/dev/swap" ))
                        && (disk->mountPoint() != QLatin1String( "/dev/pts" ))
                        && (disk->mountPoint() != QLatin1String( "/dev/shm" ))
296 297
                        && (!disk->mountPoint().startsWith(QLatin1String( "/sys/" )) )
                        && (!disk->mountPoint().startsWith(QLatin1String( "/proc/" )) ) )
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
                {
                    replaceDeviceEntry(disk);
                }
                else
                {
                    delete disk;
                }

            } //if not empty
        } //while
        f.close();
    } //if f.open

    loadSettings(); //to get the mountCommands

    return 1;
314 315 316 317 318 319 320
}


/***************************************************************************
  * reads the df-commands results
**/
int DiskList::readDF()
Dirk Mueller's avatar
Dirk Mueller committed
321
{
322
    qCDebug(KDF);
323 324 325 326 327 328 329

    if (readingDFStdErrOut || (dfProc->state() != QProcess::NotRunning))
        return -1;

    dfProc->clearProgram();

    QStringList dfenv;
330 331 332 333 334 335
    dfenv << QLatin1String( "LANG=en_US" );
    dfenv << QLatin1String( "LC_ALL=en_US" );
    dfenv << QLatin1String( "LC_MESSAGES=en_US" );
    dfenv << QLatin1String( "LC_TYPE=en_US" );
    dfenv << QLatin1String( "LANGUAGE=en_US" );
    dfenv << QLatin1String( "LC_ALL=POSIX" );
336
    dfProc->setEnvironment(dfenv);
337
    dfProc->setProgram(DF_Command,QString(DF_Args).split(QLatin1Char( ' ' )));
338 339 340 341 342 343
    dfProc->start();

    if (!dfProc->waitForStarted(-1))
        qFatal("%s", qPrintable(i18n("could not execute [%1]", QLatin1String(DF_Command))));

    return 1;
344 345 346 347 348 349 350 351
}


/***************************************************************************
  * is called, when the df-command has finished
**/
void DiskList::dfDone()
{
352
    qCDebug(KDF);
353 354 355 356 357

    if (updatesDisabled)
        return; //Don't touch the data for now..

    readingDFStdErrOut=true;
358

359 360 361 362 363 364 365 366 367 368 369
    DisksConstIterator itr = disksConstIteratorBegin();
    DisksConstIterator end = disksConstIteratorEnd();
    for (; itr != end; ++itr)
    {
        DiskEntry * disk = *itr;
        disk->setMounted(false);  // set all devs unmounted
    }

    QString dfStringErrOut = QString::fromLatin1(dfProc->readAllStandardOutput());
    QTextStream t (&dfStringErrOut, QIODevice::ReadOnly);

370
    qCDebug(KDF) << t.status();
371

372 373 374 375 376 377 378 379
    QString s;
    while ( !t.atEnd() )
    {
        s = t.readLine();
        if ( s.left(10) == QLatin1String( "Filesystem" ) )
            break;
    }
    if ( t.atEnd() )
380
        qFatal("Error running df command... got [%s]",qPrintable(s));
381

382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
    while ( !t.atEnd() )
    {
        QString u,v;
        DiskEntry *disk;
        s=t.readLine();
        s=s.simplified();
        if ( !s.isEmpty() )
        {
            disk = new DiskEntry(); //Q_CHECK_PTR(disk);

            if (!s.contains(Blank))      // devicename was too long, rest in next line
                if ( !t.atEnd() )
                {       // just appends the next line
                    v=t.readLine();
                    s=s.append(v );
                    s=s.simplified();
                }//if silly linefeed


            disk->setDeviceName(s.left(s.indexOf(Blank)) );
            s=s.remove(0,s.indexOf(Blank)+1 );

            if (No_FS_Type)
            {
406
                disk->setFsType(QLatin1String( "?" ));
407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
            }
            else
            {
                disk->setFsType(s.left(s.indexOf(Blank)) );
                s=s.remove(0,s.indexOf(Blank)+1 );
            };

            u=s.left(s.indexOf(Blank));
            disk->setKBSize(u.toULongLong() );
            s=s.remove(0,s.indexOf(Blank)+1 );

            u=s.left(s.indexOf(Blank));
            disk->setKBUsed(u.toULongLong() );
            s=s.remove(0,s.indexOf(Blank)+1 );

            u=s.left(s.indexOf(Blank));
            disk->setKBAvail(u.toULongLong() );
            s=s.remove(0,s.indexOf(Blank)+1 );


            s=s.remove(0,s.indexOf(Blank)+1 );  // delete the capacity 94%
            disk->setMountPoint(s);

            if ( (disk->kBSize() > 0)
431 432 433
                    && (disk->deviceName() != QLatin1String( "none" ))
                    && (disk->fsType() != QLatin1String( "swap" ))
                    && (disk->fsType() != QLatin1String( "sysfs" ))
434 435 436 437
                    && (disk->fsType() != QLatin1String( "rootfs" ))
                    && (disk->fsType() != QLatin1String( "tmpfs" ))
                    && (disk->fsType() != QLatin1String( "debugfs" ))
                    && (disk->fsType() != QLatin1String( "devtmpfs" ))
438 439 440
                    && (disk->mountPoint() != QLatin1String( "/dev/swap" ))
                    && (disk->mountPoint() != QLatin1String( "/dev/pts" ))
                    && (disk->mountPoint() != QLatin1String( "/dev/shm" ))
441 442
                    && (!disk->mountPoint().startsWith(QLatin1String( "/sys/" )) )
                    && (!disk->mountPoint().startsWith(QLatin1String( "/proc/" )) ) )
443
            {
Frederik Schwarzer's avatar
Frederik Schwarzer committed
444
                disk->setMounted(true);    // it is now mounted (df lists only mounted)
445 446 447 448 449 450 451 452 453 454 455 456 457
                replaceDeviceEntry(disk);
            }
            else
            {
                delete disk;
            }

        }//if not header
    }//while further lines available

    readingDFStdErrOut=false;
    loadSettings(); //to get the mountCommands
    emit readDFDone();
458 459
}

460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
int DiskList::find( DiskEntry* item )
{

    int pos = -1;
    int i = 0;

    DisksConstIterator itr = disksConstIteratorBegin();
    DisksConstIterator end = disksConstIteratorEnd();
    for (; itr != end; ++itr)
    {
        DiskEntry * disk = *itr;
        if ( *item==*disk )
        {
            pos = i;
            break;
        }
        i++;
    }

    return pos;
}
481

482 483
void DiskList::deleteAllMountedAt(const QString &mountpoint)
{
484
    qCDebug(KDF);
485

486 487 488
    DisksIterator itr = disksIteratorBegin();
    DisksIterator end = disksIteratorEnd();
    while( itr != end)
489
    {
490 491 492 493 494 495
        DisksIterator prev = itr;
        ++itr;

        DiskEntry * disk = *prev;
        if (disk->mountPoint() == mountpoint )
        {
496
            disks->removeOne( disk );
497 498
            delete disk;
        }
499 500 501
    }
}

502 503 504
/***************************************************************************
  * updates or creates a new DiskEntry in the KDFList and TabListBox
**/
505
void DiskList::replaceDeviceEntry(DiskEntry * disk)
Dirk Mueller's avatar
Dirk Mueller committed
506
{
507 508 509 510 511 512 513

    //
    // The 'disks' may already already contain the 'disk'. If it do
    // we will replace some data. Otherwise 'disk' will be added to the list
    //

    int pos = -1;
514
    uint i = 0;
515 516 517 518

    DisksConstIterator itr = disksConstIteratorBegin();
    DisksConstIterator end = disksConstIteratorEnd();
    for (; itr != end; ++itr)
519
    {
520 521 522 523 524 525 526
        DiskEntry * item = *itr;
        if( disk->realCompare(*item) )
        {
            pos = i;
            break;
        }
        i++;
527
    }
528 529 530

    if ((pos == -1) && (disk->mounted()) )
        // no matching entry found for mounted disk
531
        if ((disk->fsType() == QLatin1String( "?" )) || (disk->fsType() == QLatin1String( "cachefs" )))
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546
        {
            //search for fitting cachefs-entry in static /etc/vfstab-data
            DiskEntry* olddisk;

            DisksConstIterator itr = disksConstIteratorBegin();
            DisksConstIterator end = disksConstIteratorEnd();
            for (; itr != end; ++itr)
            {
                int p;
                // cachefs deviceNames have no / behind the host-column
                // eg. /cache/cache/.cfs_mnt_points/srv:_home_jesus
                //                                      ^    ^
                olddisk = *itr;

                QString odiskName = olddisk->deviceName();
547 548
                int ci=odiskName.indexOf(QLatin1Char( ':' )); // goto host-column
                while ((ci =odiskName.indexOf(QLatin1Char( '/' ),ci)) > 0)
549
                {
550
                    odiskName.replace(ci,1,QLatin1String( "_" ));
551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569
                }//while
                // check if there is something that is exactly the tail
                // eg. [srv:/tmp3] is exact tail of [/cache/.cfs_mnt_points/srv:_tmp3]
                if ( ( (p=disk->deviceName().lastIndexOf(odiskName
                          ,disk->deviceName().length()) )
                        != -1)
                        && (p + odiskName.length()
                            == disk->deviceName().length()) )
                {
                    pos = disks->indexOf(disk); //store the actual position
                    disk->setDeviceName(olddisk->deviceName());
                }
                //else  olddisk=disks->next();
            }// while
        }// if fsType == "?" or "cachefs"


#ifdef No_FS_Type
    if (pos != -1)
570
    {
571 572 573
        DiskEntry * olddisk = disks->at(pos);
        if (olddisk)
            disk->setFsType(olddisk->fsType());
574
    }
Dirk Mueller's avatar
Dirk Mueller committed
575
#endif
576

577 578 579
    if (pos != -1)
    {  // replace
        DiskEntry * olddisk = disks->at(pos);
580 581
        if ( (olddisk->mountOptions().contains(QLatin1String( "user" ))) &&
                ( disk->mountOptions().contains(QLatin1String( "user" ))) )
582 583 584 585
        {
            // add "user" option to new diskEntry
            QString s=disk->mountOptions();
            if (s.length()>0)
586 587
                s.append(QLatin1String( "," ));
            s.append(QLatin1String( "user" ));
588 589 590 591 592 593 594 595 596
            disk->setMountOptions(s);
        }
        disk->setMountCommand(olddisk->mountCommand());
        disk->setUmountCommand(olddisk->umountCommand());

        // Same device name, but maybe one is a symlink and the other is its target
        // Keep the shorter one then, /dev/hda1 looks better than /dev/ide/host0/bus0/target0/lun0/part1
        // but redefine "shorter" to be the number of slashes in the path as a count on characters
        // breaks legitimate symlinks created by udev
597
        if ( disk->deviceName().count( QLatin1Char( '/' ) ) > olddisk->deviceName().count( QLatin1Char( '/' ) ) )
598 599 600 601 602 603 604 605 606 607 608 609 610 611 612
            disk->setDeviceName(olddisk->deviceName());

        //FStab after an older DF ... needed for critFull
        //so the DF-KBUsed survive a FStab lookup...
        //but also an unmounted disk may then have a kbused set...
        if ( (olddisk->mounted()) && (!disk->mounted()) )
        {
            disk->setKBSize(olddisk->kBSize());
            disk->setKBUsed(olddisk->kBUsed());
            disk->setKBAvail(olddisk->kBAvail());
        }
        if ( (olddisk->percentFull() != -1) &&
                (olddisk->percentFull() <  Full_Percent) &&
                (disk->percentFull() >= Full_Percent) )
        {
613
            qCDebug(KDF) << "Device " << disk->deviceName()
614 615
            << " is critFull! " << olddisk->percentFull()
            << "--" << disk->percentFull() << endl;
616
            emit criticallyFull(disk);
617
        }
618

619 620 621
        //Take the diskentry from the list and delete it properly
        DiskEntry * tmp = disks->takeAt(pos);
        delete tmp;
622

623 624 625 626 627 628
        disks->insert(pos,disk);
    }
    else
    {
        disks->append(disk);
    }//if
629

630
}
631

632
#include "disklist.moc"
633