ProcessInfo.cpp 28.3 KB
Newer Older
1
/*
2
    Copyright 2007-2008 by Robert Knight <robertknight@gmail.countm>
3
4
5
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
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301  USA.
*/

20
21
22
// Config
#include <config-konsole.h>

23
24
25
// Own
#include "ProcessInfo.h"

26
27
28
29
// Unix
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
30
31
#include <unistd.h>
#include <pwd.h>
32

33
// Qt
34
#include <QtCore/QDir>
Dirk Mueller's avatar
Dirk Mueller committed
35
36
37
#include <QtCore/QFileInfo>
#include <QtCore/QTextStream>
#include <QtCore/QStringList>
38
#include <QtNetwork/QHostInfo>
39

40
41
42
43
// KDE
#include <KConfigGroup>
#include <KGlobal>
#include <KSharedConfig>
44
#include <KUser>
Jekyll Wu's avatar
Jekyll Wu committed
45
#include <KDebug>
46

47
48
#if defined(Q_OS_MAC)
#include <sys/sysctl.h>
49
#include <libproc.h>
50
#ifdef HAVE_SYS_PROC_INFO_H
51
#include <sys/proc_info.h>
52
53
#endif
#ifdef HAVE_SYS_PROC_H
54
#include <sys/proc.h>
55
#endif
56
#include <kde_file.h>
57
58
#endif

59
#if defined(Q_OS_FREEBSD)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
60
#include <sys/sysctl.h>
61
62
#include <sys/types.h>
#include <sys/user.h>
63
#include <sys/syslimits.h>
64
65
66
#include <libutil.h>
#endif

67
using namespace Konsole;
68

69
ProcessInfo::ProcessInfo(int aPid , bool enableEnvironmentRead)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
70
71
72
73
74
75
76
    : _fields(ARGUMENTS | ENVIRONMENT)   // arguments and environments
    // are currently always valid,
    // they just return an empty
    // vector / map respectively
    // if no arguments
    // or environment bindings
    // have been explicitly set
77
    , _enableEnvironmentRead(enableEnvironmentRead)
78
    , _pid(aPid)
79
80
    , _parentPid(0)
    , _foregroundPid(0)
81
    , _userId(0)
82
    , _lastError(NoError)
83
    , _userName(QString())
84
    , _userHomeDir(QString())
85
86
87
{
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
88
89
90
91
92
93
94
95
ProcessInfo::Error ProcessInfo::error() const
{
    return _lastError;
}
void ProcessInfo::setError(Error error)
{
    _lastError = error;
}
96

Kurt Hindenburg's avatar
Kurt Hindenburg committed
97
void ProcessInfo::update()
98
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
99
    readProcessInfo(_pid, _enableEnvironmentRead);
100
101
}

Robert Knight's avatar
   
Robert Knight committed
102
QString ProcessInfo::validCurrentDir() const
103
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
    bool ok = false;

    // read current dir, if an error occurs try the parent as the next
    // best option
    int currentPid = parentPid(&ok);
    QString dir = currentDir(&ok);
    while (!ok && currentPid != 0) {
        ProcessInfo* current = ProcessInfo::newInstance(currentPid);
        current->update();
        currentPid = current->parentPid(&ok);
        dir = current->currentDir(&ok);
        delete current;
    }

    return dir;
Robert Knight's avatar
   
Robert Knight committed
119
120
121
122
}

QString ProcessInfo::format(const QString& input) const
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
123
124
125
126
127
128
    bool ok = false;

    QString output(input);

    // search for and replace known marker
    output.replace("%u", userName());
129
    output.replace("%h", localHost());
Kurt Hindenburg's avatar
Kurt Hindenburg committed
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
    output.replace("%n", name(&ok));

    QString dir = validCurrentDir();
    if (output.contains("%D")) {
        QString homeDir = userHomeDir();
        QString tempDir = dir;
        // Change User's Home Dir w/ ~ only at the beginning
        if (tempDir.startsWith(homeDir)) {
            tempDir.remove(0, homeDir.length());
            tempDir.prepend('~');
        }
        output.replace("%D", tempDir);
    }
    output.replace("%d", formatShortDir(dir));

    return output;
146
147
}

148
149
QSet<QString> ProcessInfo::_commonDirNames;

Kurt Hindenburg's avatar
Kurt Hindenburg committed
150
QSet<QString> ProcessInfo::commonDirNames()
151
{
152
    static bool forTheFirstTime = true;
153

Kurt Hindenburg's avatar
Kurt Hindenburg committed
154
    if (forTheFirstTime) {
155
156
        const KSharedConfigPtr& config = KGlobal::config();
        const KConfigGroup& configGroup = config->group("ProcessInfo");
157
        _commonDirNames = QSet<QString>::fromList(configGroup.readEntry("CommonDirNames", QStringList()));
158

159
        forTheFirstTime = false;
160
161
162
163
164
    }

    return _commonDirNames;
}

165
166
167
168
QString ProcessInfo::formatShortDir(const QString& input) const
{
    QString result;

Jekyll Wu's avatar
Jekyll Wu committed
169
    const QStringList& parts = input.split(QDir::separator());
170

171
    QSet<QString> dirNamesToShorten = commonDirNames();
172
173
174
175
176
177
178
179

    QListIterator<QString> iter(parts);
    iter.toBack();

    // go backwards through the list of the path's parts
    // adding abbreviations of common directory names
    // and stopping when we reach a dir name which is not
    // in the commonDirNames set
Kurt Hindenburg's avatar
Kurt Hindenburg committed
180
    while (iter.hasPrevious()) {
Jekyll Wu's avatar
Jekyll Wu committed
181
        const QString& part = iter.previous();
182

Kurt Hindenburg's avatar
Kurt Hindenburg committed
183
        if (dirNamesToShorten.contains(part)) {
184
            result.prepend(QDir::separator() + part[0]);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
185
        } else {
186
187
188
189
190
191
192
193
            result.prepend(part);
            break;
        }
    }

    return result;
}

194
195
196
197
198
199
200
QVector<QString> ProcessInfo::arguments(bool* ok) const
{
    *ok = _fields & ARGUMENTS;

    return _arguments;
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
201
QMap<QString, QString> ProcessInfo::environment(bool* ok) const
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
{
    *ok = _fields & ENVIRONMENT;

    return _environment;
}

bool ProcessInfo::isValid() const
{
    return _fields & PROCESS_ID;
}

int ProcessInfo::pid(bool* ok) const
{
    *ok = _fields & PROCESS_ID;

    return _pid;
}

int ProcessInfo::parentPid(bool* ok) const
{
    *ok = _fields & PARENT_PID;

    return _parentPid;
}

int ProcessInfo::foregroundPid(bool* ok) const
{
    *ok = _fields & FOREGROUND_PID;

    return _foregroundPid;
}

QString ProcessInfo::name(bool* ok) const
{
    *ok = _fields & NAME;

    return _name;
}

241
242
243
244
245
246
247
248
249
250
251
252
int ProcessInfo::userId(bool* ok) const
{
    *ok = _fields & UID;

    return _userId;
}

QString ProcessInfo::userName() const
{
    return _userName;
}

253
254
255
256
257
QString ProcessInfo::userHomeDir() const
{
    return _userHomeDir;
}

258
259
260
261
262
QString ProcessInfo::localHost()
{
    return QHostInfo::localHostName();
}

263
void ProcessInfo::setPid(int aPid)
264
{
265
    _pid = aPid;
266
267
268
    _fields |= PROCESS_ID;
}

269
270
271
272
273
274
275
276
277
void ProcessInfo::setUserId(int uid)
{
    _userId = uid;
    _fields |= UID;
}

void ProcessInfo::setUserName(const QString& name)
{
    _userName = name;
278
    setUserHomeDir();
279
280
}

281
282
void ProcessInfo::setUserHomeDir()
{
Jekyll Wu's avatar
Jekyll Wu committed
283
    const QString& usersName = userName();
284
285
286
287
288
    if (!usersName.isEmpty())
        _userHomeDir = KUser(usersName).homeDir();
    else
        _userHomeDir = QDir::homePath();
}
289

290
void ProcessInfo::setParentPid(int aPid)
291
{
292
    _parentPid = aPid;
293
294
    _fields |= PARENT_PID;
}
295
void ProcessInfo::setForegroundPid(int aPid)
296
{
297
    _foregroundPid = aPid;
298
299
    _fields |= FOREGROUND_PID;
}
300

301
302
QString ProcessInfo::currentDir(bool* ok) const
{
303
304
    if (ok)
        *ok = _fields & CURRENT_DIR;
305
306
307
308
309
310
311
312
313

    return _currentDir;
}
void ProcessInfo::setCurrentDir(const QString& dir)
{
    _fields |= CURRENT_DIR;
    _currentDir = dir;
}

314
315
316
317
318
319
320
void ProcessInfo::setName(const QString& name)
{
    _name = name;
    _fields |= NAME;
}
void ProcessInfo::addArgument(const QString& argument)
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
321
    _arguments << argument;
322
323
}

324
325
326
327
328
void ProcessInfo::clearArguments()
{
    _arguments.clear();
}

329
330
void ProcessInfo::addEnvironmentBinding(const QString& name , const QString& value)
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
331
    _environment.insert(name, value);
332
333
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
334
void ProcessInfo::setFileError(QFile::FileError error)
335
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
336
337
338
339
340
341
342
343
344
    switch (error) {
    case PermissionsError:
        setError(PermissionsError);
        break;
    case NoError:
        setError(NoError);
        break;
    default:
        setError(UnknownError);
345
346
347
    }
}

348
349
350
351
//
// ProcessInfo::newInstance() is way at the bottom so it can see all of the
// implementations of the UnixProcessInfo abstract class.
//
352

353
354
NullProcessInfo::NullProcessInfo(int aPid, bool enableEnvironmentRead)
    : ProcessInfo(aPid, enableEnvironmentRead)
355
356
357
358
359
360
361
362
{
}

bool NullProcessInfo::readProcessInfo(int /*pid*/ , bool /*enableEnvironmentRead*/)
{
    return false;
}

363
364
365
366
void NullProcessInfo::readUserName()
{
}

367
368
UnixProcessInfo::UnixProcessInfo(int aPid, bool enableEnvironmentRead)
    : ProcessInfo(aPid, enableEnvironmentRead)
369
{
370
}
371

372
bool UnixProcessInfo::readProcessInfo(int aPid , bool enableEnvironmentRead)
373
{
374
375
376
377
    // prevent _arguments from growing longer and longer each time this
    // method is called.
    clearArguments();

378
    bool ok = readProcInfo(aPid);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
379
    if (ok) {
380
381
        ok |= readArguments(aPid);
        ok |= readCurrentDir(aPid);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
382
        if (enableEnvironmentRead) {
383
            ok |= readEnvironment(aPid);
384
        }
385
    }
386
387
388
    return ok;
}

389
390
391
void UnixProcessInfo::readUserName()
{
    bool ok = false;
Jekyll Wu's avatar
Jekyll Wu committed
392
    const int uid = userId(&ok);
393
394
    if (!ok) return;

395
    struct passwd passwdStruct;
396
397
    struct passwd* getpwResult;
    char* getpwBuffer;
398
399
400
401
402
403
404
405
406
407
408
    long getpwBufferSize;
    int getpwStatus;

    getpwBufferSize = sysconf(_SC_GETPW_R_SIZE_MAX);
    if (getpwBufferSize == -1)
        getpwBufferSize = 16384;

    getpwBuffer = new char[getpwBufferSize];
    if (getpwBuffer == NULL)
        return;
    getpwStatus = getpwuid_r(uid, &passwdStruct, getpwBuffer, getpwBufferSize, &getpwResult);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
409
    if ((getpwStatus == 0) && (getpwResult != NULL)) {
410
        setUserName(QString(passwdStruct.pw_name));
Kurt Hindenburg's avatar
Kurt Hindenburg committed
411
    } else {
412
        setUserName(QString());
Kurt Hindenburg's avatar
Kurt Hindenburg committed
413
        kWarning() << "getpwuid_r returned error : " << getpwStatus;
414
    }
415
    delete [] getpwBuffer;
416
417
}

418
419
420
class LinuxProcessInfo : public UnixProcessInfo
{
public:
421
422
    LinuxProcessInfo(int aPid, bool env) :
        UnixProcessInfo(aPid, env) {
423
    }
424

425
private:
426
    virtual bool readProcInfo(int aPid) {
427
428
429
430
431
432
433
434
435
        // indicies of various fields within the process status file which
        // contain various information about the process
        const int PARENT_PID_FIELD = 3;
        const int PROCESS_NAME_FIELD = 1;
        const int GROUP_PROCESS_FIELD = 7;

        QString parentPidString;
        QString processNameString;
        QString foregroundPidString;
436
437
438
        QString uidLine;
        QString uidString;
        QStringList uidStrings;
439

440
441
        // For user id read process status file ( /proc/<pid>/status )
        //  Can not use getuid() due to it does not work for 'su'
442
        QFile statusInfo(QString("/proc/%1/status").arg(aPid));
Kurt Hindenburg's avatar
Kurt Hindenburg committed
443
        if (statusInfo.open(QIODevice::ReadOnly)) {
444
            QTextStream stream(&statusInfo);
445
446
447
            QString statusLine;
            do {
                statusLine = stream.readLine(0);
448
                if (statusLine.startsWith(QLatin1String("Uid:")))
449
450
451
452
453
454
455
456
457
458
459
                    uidLine = statusLine;
            } while (!statusLine.isNull() && uidLine.isNull());

            uidStrings << uidLine.split('\t', QString::SkipEmptyParts);
            // Must be 5 entries: 'Uid: %d %d %d %d' and
            // uid string must be less than 5 chars (uint)
            if (uidStrings.size() == 5)
                uidString = uidStrings[1];
            if (uidString.size() > 5)
                uidString.clear();

460
            bool ok = false;
Jekyll Wu's avatar
Jekyll Wu committed
461
            const int uid = uidString.toInt(&ok);
462
463
464
            if (ok)
                setUserId(uid);
            readUserName();
Kurt Hindenburg's avatar
Kurt Hindenburg committed
465
466
        } else {
            setFileError(statusInfo.error());
467
468
469
            return false;
        }

470
471
472
473
474
475
476
477
        // read process status file ( /proc/<pid/stat )
        //
        // the expected file format is a list of fields separated by spaces, using
        // parenthesies to escape fields such as the process name which may itself contain
        // spaces:
        //
        // FIELD FIELD (FIELD WITH SPACES) FIELD FIELD
        //
478
        QFile processInfo(QString("/proc/%1/stat").arg(aPid));
Kurt Hindenburg's avatar
Kurt Hindenburg committed
479
        if (processInfo.open(QIODevice::ReadOnly)) {
480
            QTextStream stream(&processInfo);
Jekyll Wu's avatar
Jekyll Wu committed
481
            const QString& data = stream.readAll();
482

483
484
485
486
            int stack = 0;
            int field = 0;
            int pos = 0;

Kurt Hindenburg's avatar
Kurt Hindenburg committed
487
            while (pos < data.count()) {
488
489
                QChar c = data[pos];

Kurt Hindenburg's avatar
Kurt Hindenburg committed
490
                if (c == '(')
491
                    stack++;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
492
                else if (c == ')')
493
                    stack--;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
494
                else if (stack == 0 && c == ' ')
495
                    field++;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
496
497
498
499
500
501
502
503
504
505
506
                else {
                    switch (field) {
                    case PARENT_PID_FIELD:
                        parentPidString.append(c);
                        break;
                    case PROCESS_NAME_FIELD:
                        processNameString.append(c);
                        break;
                    case GROUP_PROCESS_FIELD:
                        foregroundPidString.append(c);
                        break;
507
508
                    }
                }
509

510
511
                pos++;
            }
Kurt Hindenburg's avatar
Kurt Hindenburg committed
512
513
        } else {
            setFileError(processInfo.error());
514
515
            return false;
        }
516

517
518
        // check that data was read successfully
        bool ok = false;
Jekyll Wu's avatar
Jekyll Wu committed
519
        const int foregroundPid = foregroundPidString.toInt(&ok);
520
521
522
        if (ok)
            setForegroundPid(foregroundPid);

Jekyll Wu's avatar
Jekyll Wu committed
523
        const int parentPid = parentPidString.toInt(&ok);
524
525
526
527
528
529
530
        if (ok)
            setParentPid(parentPid);

        if (!processNameString.isEmpty())
            setName(processNameString);

        // update object state
531
        setPid(aPid);
532
533
534
535

        return ok;
    }

536
    virtual bool readArguments(int aPid) {
537
538
539
540
        // read command-line arguments file found at /proc/<pid>/cmdline
        // the expected format is a list of strings delimited by null characters,
        // and ending in a double null character pair.

541
        QFile argumentsFile(QString("/proc/%1/cmdline").arg(aPid));
Kurt Hindenburg's avatar
Kurt Hindenburg committed
542
        if (argumentsFile.open(QIODevice::ReadOnly)) {
543
            QTextStream stream(&argumentsFile);
Jekyll Wu's avatar
Jekyll Wu committed
544
            const QString& data = stream.readAll();
545

Jekyll Wu's avatar
Jekyll Wu committed
546
            const QStringList& argList = data.split(QChar('\0'));
547

Kurt Hindenburg's avatar
Kurt Hindenburg committed
548
            foreach(const QString & entry , argList) {
549
550
551
                if (!entry.isEmpty())
                    addArgument(entry);
            }
Kurt Hindenburg's avatar
Kurt Hindenburg committed
552
553
        } else {
            setFileError(argumentsFile.error());
554
555
        }

556
557
        return true;
    }
558

559
560
    virtual bool readCurrentDir(int aPid) {
        QFileInfo info(QString("/proc/%1/cwd").arg(aPid));
561
562
563

        const bool readable = info.isReadable();

Kurt Hindenburg's avatar
Kurt Hindenburg committed
564
565
        if (readable && info.isSymLink()) {
            setCurrentDir(info.symLinkTarget());
566
            return true;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
567
568
569
        } else {
            if (!readable)
                setError(PermissionsError);
570
            else
Kurt Hindenburg's avatar
Kurt Hindenburg committed
571
                setError(UnknownError);
572

573
574
575
            return false;
        }
    }
576

577
    virtual bool readEnvironment(int aPid) {
578
579
580
        // read environment bindings file found at /proc/<pid>/environ
        // the expected format is a list of KEY=VALUE strings delimited by null
        // characters and ending in a double null character pair.
581

582
        QFile environmentFile(QString("/proc/%1/environ").arg(aPid));
Kurt Hindenburg's avatar
Kurt Hindenburg committed
583
        if (environmentFile.open(QIODevice::ReadOnly)) {
584
            QTextStream stream(&environmentFile);
Jekyll Wu's avatar
Jekyll Wu committed
585
            const QString& data = stream.readAll();
586

Jekyll Wu's avatar
Jekyll Wu committed
587
            const QStringList& bindingList = data.split(QChar('\0'));
588

Kurt Hindenburg's avatar
Kurt Hindenburg committed
589
            foreach(const QString & entry , bindingList) {
590
591
592
                QString name;
                QString value;

Jekyll Wu's avatar
Jekyll Wu committed
593
                const int splitPos = entry.indexOf('=');
594

Kurt Hindenburg's avatar
Kurt Hindenburg committed
595
596
597
                if (splitPos != -1) {
                    name = entry.mid(0, splitPos);
                    value = entry.mid(splitPos + 1, -1);
598

Kurt Hindenburg's avatar
Kurt Hindenburg committed
599
                    addEnvironmentBinding(name, value);
600
601
                }
            }
Kurt Hindenburg's avatar
Kurt Hindenburg committed
602
603
        } else {
            setFileError(environmentFile.error());
604
        }
605
606

        return true;
607
    }
608
};
609

610
611
612
613
#if defined(Q_OS_FREEBSD)
class FreeBSDProcessInfo : public UnixProcessInfo
{
public:
614
615
    FreeBSDProcessInfo(int aPid, bool readEnvironment) :
        UnixProcessInfo(aPid, readEnvironment) {
616
617
618
    }

private:
619
    virtual bool readProcInfo(int aPid) {
620
621
622
623
624
625
626
        int managementInfoBase[4];
        size_t mibLength;
        struct kinfo_proc* kInfoProc;

        managementInfoBase[0] = CTL_KERN;
        managementInfoBase[1] = KERN_PROC;
        managementInfoBase[2] = KERN_PROC_PID;
627
        managementInfoBase[3] = aPid;
628
629
630
631
632
633

        if (sysctl(managementInfoBase, 4, NULL, &mibLength, NULL, 0) == -1)
            return false;

        kInfoProc = new struct kinfo_proc [mibLength];

Kurt Hindenburg's avatar
Kurt Hindenburg committed
634
        if (sysctl(managementInfoBase, 4, kInfoProc, &mibLength, NULL, 0) == -1) {
635
636
637
638
            delete [] kInfoProc;
            return false;
        }

639
640
641
642
643
644
645
#if defined(__DragonFly__)
        setName(kInfoProc->kp_comm);
        setPid(kInfoProc->kp_pid);
        setParentPid(kInfoProc->kp_ppid);
        setForegroundPid(kInfoProc->kp_pgid);
        setUserId(kInfoProc->kp_uid);
#else
646
647
648
649
650
        setName(kInfoProc->ki_comm);
        setPid(kInfoProc->ki_pid);
        setParentPid(kInfoProc->ki_ppid);
        setForegroundPid(kInfoProc->ki_pgid);
        setUserId(kInfoProc->ki_uid);
651
#endif
652
653
654
655
656
657
658

        readUserName();

        delete [] kInfoProc;
        return true;
    }

659
    virtual bool readArguments(int aPid) {
660
661
662
663
664
665
666
        char args[ARG_MAX];
        int managementInfoBase[4];
        size_t len;

        managementInfoBase[0] = CTL_KERN;
        managementInfoBase[1] = KERN_PROC;
        managementInfoBase[2] = KERN_PROC_PID;
667
        managementInfoBase[3] = aPid;
668
669
670
671
672

        len = sizeof(args);
        if (sysctl(managementInfoBase, 4, args, &len, NULL, 0) == -1)
            return false;

Jekyll Wu's avatar
Jekyll Wu committed
673
        const QStringList& argumentList = QString(args).split(QChar('\0'));
674

Kurt Hindenburg's avatar
Kurt Hindenburg committed
675
        for (QStringList::const_iterator it = argumentList.begin(); it != argumentList.end(); ++it) {
676
677
678
679
            addArgument(*it);
        }

        return true;
680
681
    }

682
    virtual bool readEnvironment(int aPid) {
683
684
685
686
        // Not supported in FreeBSD?
        return false;
    }

687
    virtual bool readCurrentDir(int aPid) {
688
#if defined(__DragonFly__)
689
690
691
692
693
694
695
        char buf[PATH_MAX];
        int managementInfoBase[4];
        size_t len;

        managementInfoBase[0] = CTL_KERN;
        managementInfoBase[1] = KERN_PROC;
        managementInfoBase[2] = KERN_PROC_CWD;
696
        managementInfoBase[3] = aPid;
697
698
699
700
701
702
703
704

        len = sizeof(buf);
        if (sysctl(managementInfoBase, 4, buf, &len, NULL, 0) == -1)
            return false;

        setCurrentDir(buf);

        return true;
705
#else
706
707
708
        int numrecords;
        struct kinfo_file* info = 0;

709
        info = kinfo_getfile(aPid, &numrecords);
710
711
712
713

        if (!info)
            return false;

Kurt Hindenburg's avatar
Kurt Hindenburg committed
714
715
        for (int i = 0; i < numrecords; ++i) {
            if (info[i].kf_fd == KF_FD_TYPE_CWD) {
716
                setCurrentDir(info[i].kf_path);
717
718
719
720
721
722
723
724

                free(info);
                return true;
            }
        }

        free(info);
        return false;
725
#endif
726
    }
727
};
728
729
#endif

730
#if defined(Q_OS_MAC)
731
732
733
class MacProcessInfo : public UnixProcessInfo
{
public:
734
735
    MacProcessInfo(int aPid, bool env) :
        UnixProcessInfo(aPid, env) {
736
737
738
    }

private:
739
    virtual bool readProcInfo(int aPid) {
740
741
742
        int managementInfoBase[4];
        size_t mibLength;
        struct kinfo_proc* kInfoProc;
743
        KDE_struct_stat statInfo;
744
745
746
747
748

        // Find the tty device of 'pid' (Example: /dev/ttys001)
        managementInfoBase[0] = CTL_KERN;
        managementInfoBase[1] = KERN_PROC;
        managementInfoBase[2] = KERN_PROC_PID;
749
        managementInfoBase[3] = aPid;
750

Kurt Hindenburg's avatar
Kurt Hindenburg committed
751
        if (sysctl(managementInfoBase, 4, NULL, &mibLength, NULL, 0) == -1) {
752
            return false;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
753
        } else {
754
            kInfoProc = new struct kinfo_proc [mibLength];
Kurt Hindenburg's avatar
Kurt Hindenburg committed
755
            if (sysctl(managementInfoBase, 4, kInfoProc, &mibLength, NULL, 0) == -1) {
756
757
                delete [] kInfoProc;
                return false;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
758
            } else {
Jekyll Wu's avatar
Jekyll Wu committed
759
760
                const QString deviceNumber = QString(devname(((&kInfoProc->kp_eproc)->e_tdev), S_IFCHR));
                const QString fullDeviceName =  QString("/dev/") + deviceNumber.rightJustified(3, '0');
761
762
                delete [] kInfoProc;

Jekyll Wu's avatar
Jekyll Wu committed
763
                const QByteArray deviceName = fullDeviceName.toLatin1();
764
                const char* ttyName = deviceName.data();
765

Kurt Hindenburg's avatar
Kurt Hindenburg committed
766
                if (KDE::stat(ttyName, &statInfo) != 0)
767
768
769
770
771
772
773
774
775
                    return false;

                // Find all processes attached to ttyName
                managementInfoBase[0] = CTL_KERN;
                managementInfoBase[1] = KERN_PROC;
                managementInfoBase[2] = KERN_PROC_TTY;
                managementInfoBase[3] = statInfo.st_rdev;

                mibLength = 0;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
776
                if (sysctl(managementInfoBase, sizeof(managementInfoBase) / sizeof(int), NULL, &mibLength, NULL, 0) == -1)
777
778
779
                    return false;

                kInfoProc = new struct kinfo_proc [mibLength];
Kurt Hindenburg's avatar
Kurt Hindenburg committed
780
                if (sysctl(managementInfoBase, sizeof(managementInfoBase) / sizeof(int), kInfoProc, &mibLength, NULL, 0) == -1)
781
782
783
784
785
786
787
788
789
790
791
                    return false;

                // The foreground program is the first one
                setName(QString(kInfoProc->kp_proc.p_comm));

                delete [] kInfoProc;
            }
        }
        return true;
    }

792
793
    virtual bool readArguments(int aPid) {
        Q_UNUSED(aPid);
794
795
        return false;
    }
796
    virtual bool readCurrentDir(int aPid) {
797
        struct proc_vnodepathinfo vpi;
798
        const int nb = proc_pidinfo(aPid, PROC_PIDVNODEPATHINFO, 0, &vpi, sizeof(vpi));
Kurt Hindenburg's avatar
Kurt Hindenburg committed
799
        if (nb == sizeof(vpi)) {
800
801
802
            setCurrentDir(QString(vpi.pvi_cdir.vip_path));
            return true;
        }
803
804
        return false;
    }
805
806
    virtual bool readEnvironment(int aPid) {
        Q_UNUSED(aPid);
807
808
        return false;
    }
809
};
810
#endif
811

812
#ifdef Q_OS_SOLARIS
Kurt Hindenburg's avatar
Kurt Hindenburg committed
813
814
815
816
817
818
819
820
821
822
// The procfs structure definition requires off_t to be
// 32 bits, which only applies if FILE_OFFSET_BITS=32.
// Futz around here to get it to compile regardless,
// although some of the structure sizes might be wrong.
// Fortunately, the structures we actually use don't use
// off_t, and we're safe.
#if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS==64)
#undef _FILE_OFFSET_BITS
#endif
#include <procfs.h>
823
#else
Kurt Hindenburg's avatar
Kurt Hindenburg committed
824
825
826
827
828
829
830
831
832
833
834
// On non-Solaris platforms, define a fake psinfo structure
// so that the SolarisProcessInfo class can be compiled
//
// That avoids the trap where you change the API and
// don't notice it in #ifdeffed platform-specific parts
// of the code.
struct psinfo {
    int pr_ppid;
    int pr_pgid;
    char* pr_fname;
    char* pr_psargs;
835
};
Kurt Hindenburg's avatar
Kurt Hindenburg committed
836
static const int PRARGSZ = 1;
837
838
839
840
841
#endif

class SolarisProcessInfo : public UnixProcessInfo
{
public:
842
843
    SolarisProcessInfo(int aPid, bool readEnvironment)
        : UnixProcessInfo(aPid, readEnvironment) {
844
    }
845
private:
846
847
    virtual bool readProcInfo(int aPid) {
        QFile psinfo(QString("/proc/%1/psinfo").arg(aPid));
Kurt Hindenburg's avatar
Kurt Hindenburg committed
848
        if (psinfo.open(QIODevice::ReadOnly)) {
849
            struct psinfo info;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
850
            if (psinfo.read((char *)&info, sizeof(info)) != sizeof(info)) {
851
852
                return false;
            }
853

854
855
856
            setParentPid(info.pr_ppid);
            setForegroundPid(info.pr_pgid);
            setName(info.pr_fname);
857
            setPid(aPid);
858
859

            // Bogus, because we're treating the arguments as one single string
Kurt Hindenburg's avatar
Kurt Hindenburg committed
860
            info.pr_psargs[PRARGSZ - 1] = 0;
861
862
863
864
865
            addArgument(info.pr_psargs);
        }
        return true;
    }

Kurt Hindenburg's avatar
Kurt Hindenburg committed
866
    virtual bool readArguments(int /*pid*/) {
867
        // Handled in readProcInfo()
868
        return false;
869
870
    }

Kurt Hindenburg's avatar
Kurt Hindenburg committed
871
    virtual bool readEnvironment(int /*pid*/) {
872
        // Not supported in Solaris
873
        return false;
874
875
    }

876
877
    virtual bool readCurrentDir(int aPid) {
        QFileInfo info(QString("/proc/%1/path/cwd").arg(aPid));
878
879
        const bool readable = info.isReadable();

Kurt Hindenburg's avatar
Kurt Hindenburg committed
880
881
        if (readable && info.isSymLink()) {
            setCurrentDir(info.symLinkTarget());
882
            return true;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
883
884
885
        } else {
            if (!readable)
                setError(PermissionsError);
886
            else
Kurt Hindenburg's avatar
Kurt Hindenburg committed
887
                setError(UnknownError);
888
889
890
891

            return false;
        }
    }
892
};
893
894

SSHProcessInfo::SSHProcessInfo(const ProcessInfo& process)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
895
    : _process(process)
896
897
898
899
900
901
{
    bool ok = false;

    // check that this is a SSH process
    const QString& name = _process.name(&ok);

Kurt Hindenburg's avatar
Kurt Hindenburg committed
902
903
    if (!ok || name != "ssh") {
        if (!ok)
904
            kWarning() << "Could not read process info";
905
        else
906
            kWarning() << "Process is not a SSH process";
907
908
909
910
911

        return;
    }

    // read arguments
Kurt Hindenburg's avatar
Kurt Hindenburg committed
912
    const QVector<QString>& args = _process.arguments(&ok);
913
914
915

    // SSH options
    // these are taken from the SSH manual ( accessed via 'man ssh' )
916

917
    // options which take no arguments
918
    static const QString noArgumentOptions("1246AaCfgKkMNnqsTtVvXxYy");
919
    // options which take one argument
920
    static const QString singleArgumentOptions("bcDeFIiLlmOopRSWw");
921

Kurt Hindenburg's avatar
Kurt Hindenburg committed
922
923
924
925
926
927
928
929
930
931
932
933
934
935
    if (ok) {
        // find the username, host and command arguments
        //
        // the username/host is assumed to be the first argument
        // which is not an option
        // ( ie. does not start with a dash '-' character )
        // or an argument to a previous option.
        //
        // the command, if specified, is assumed to be the argument following
        // the username and host
        //
        // note that we skip the argument at index 0 because that is the
        // program name ( expected to be 'ssh' in this case )
        for (int i = 1 ; i < args.count() ; i++) {
936
937
            // If this one is an option ...
            // Most options together with its argument will be skipped.
Kurt Hindenburg's avatar
Kurt Hindenburg committed
938
            if (args[i].startsWith('-')) {
Jekyll Wu's avatar
Jekyll Wu committed
939
                const QChar optionChar = (args[i].length() > 1) ? args[i][1] : '\0';
940
                // for example: -p2222 or -p 2222 ?
941
                const bool optionArgumentCombined =  args[i].length() > 2;
942

Kurt Hindenburg's avatar
Kurt Hindenburg committed
943
                if (noArgumentOptions.contains(optionChar)) {
944
                    continue;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
945
                } else if (singleArgumentOptions.contains(optionChar)) {
946
                    QString argument;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
947
                    if (optionArgumentCombined) {
948
                        argument = args[i].mid(2);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
949
                    } else {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
950
951
                        // Verify correct # arguments are given
                        if ((i + 1) < args.count()) {
952
                            argument = args[i + 1];
Kurt Hindenburg's avatar
Kurt Hindenburg committed
953
                        }
954
955
956
                        i++;
                    }

957
                    // support using `-l user` to specify username.
Kurt Hindenburg's avatar
Kurt Hindenburg committed
958
                    if (optionChar == 'l')
959
                        _user = argument;
960
                    // support using `-p port` to specify port.
Kurt Hindenburg's avatar
Kurt Hindenburg committed
961
                    else if (optionChar == 'p')
962
                        _port = argument;
963

964
965
966
967
968
                    continue;
                }
            }

            // check whether the host has been found yet
Kurt Hindenburg's avatar
Kurt Hindenburg committed
969
970
            // if not, this must be the username/host argument
            if (_host.isEmpty()) {
971
972
973
                // check to see if only a hostname is specified, or whether
                // both a username and host are specified ( in which case they
                // are separated by an '@' character:  username@host )
974

Jekyll Wu's avatar
Jekyll Wu committed
975
                const int separatorPosition = args[i].indexOf('@');
Kurt Hindenburg's avatar
Kurt Hindenburg committed
976
                if (separatorPosition != -1) {
977
978
                    // username and host specified
                    _user = args[i].left(separatorPosition);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
979
980
                    _host = args[i].mid(separatorPosition + 1);
                } else {
981
982
983
                    // just the host specified
                    _host = args[i];
                }
Kurt Hindenburg's avatar
Kurt Hindenburg committed
984
            } else {
985
986
987
                // host has already been found, this must be the command argument
                _command = args[i];
            }
Kurt Hindenburg's avatar
Kurt Hindenburg committed
988
989
        }
    } else {
990
        kWarning() << "Could not read arguments";
991

992
993
994
995
996
997
998
999
1000
1001
1002
1003
        return;
    }
}

QString SSHProcessInfo::userName() const
{
    return _user;
}
QString SSHProcessInfo::host() const
{
    return _host;
}
1004
1005
1006
1007
QString SSHProcessInfo::port() const
{
    return _port;
}
1008
1009
1010
1011
QString SSHProcessInfo::command() const
{
    return _command;
}
1012
1013
1014
QString SSHProcessInfo::format(const QString& input) const
{
    QString output(input);
1015

1016
1017
1018
1019
1020
    // test whether host is an ip address
    // in which case 'short host' and 'full host'
    // markers in the input string are replaced with
    // the full address
    bool isIpAddress = false;
1021

1022
    struct in_addr address;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
1023
    if (inet_aton(_host.toLocal8Bit