MauiKit Terminal

ProcessInfo.cpp
1/*
2 SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.countm>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5
6 This program is distributed in the hope that it will be useful,
7 but WITHOUT ANY WARRANTY; without even the implied warranty of
8 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 GNU General Public License for more details.
10
11 You should have received a copy of the GNU General Public License
12 along with this program; if not, write to the Free Software
13 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
14 02110-1301 USA.
15*/
16
17// Own
18#include "ProcessInfo.h"
19
20// Unix
21#include <arpa/inet.h>
22#include <netinet/in.h>
23#include <pwd.h>
24#include <sys/param.h>
25#include <sys/socket.h>
26#include <unistd.h>
27
28// Qt
29#include <QtCore/QDir>
30#include <QtCore/QFileInfo>
31#include <QtCore/QFlags>
32#include <QtCore/QStringList>
33#include <QtCore/QTextStream>
34#include <QtNetwork/QHostInfo>
35
36// KDE
37// #include <KConfigGroup>
38// #include <KSharedConfig>
39#include <QDebug>
40
41#if defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) || defined(Q_OS_MAC)
42#include <sys/sysctl.h>
43#endif
44
45#if defined(Q_OS_MAC)
46#include <libproc.h>
47#endif
48
49#if defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
50#include <sys/syslimits.h>
51#include <sys/types.h>
52#include <sys/user.h>
53#if defined(Q_OS_FREEBSD)
54#include <libutil.h>
55#endif
56#endif
57
58using namespace Konsole;
59
60ProcessInfo::ProcessInfo(int aPid, bool enableEnvironmentRead)
61 : _fields(ARGUMENTS | ENVIRONMENT) // arguments and environments
62 // are currently always valid,
63 // they just return an empty
64 // vector / map respectively
65 // if no arguments
66 // or environment bindings
67 // have been explicitly set
68 , _enableEnvironmentRead(enableEnvironmentRead)
69 , _pid(aPid)
70 , _parentPid(0)
71 , _foregroundPid(0)
72 , _userId(0)
73 , _lastError(NoError)
74 , _userName(QString())
75 , _userHomeDir(QString())
76{
77}
78
80{
81 return _lastError;
82}
84{
85 _lastError = error;
86}
87
89{
90 readProcessInfo(_pid, _enableEnvironmentRead);
91}
92
94{
95 bool ok = false;
96
97 // read current dir, if an error occurs try the parent as the next
98 // best option
99 int currentPid = parentPid(&ok);
100 QString dir = currentDir(&ok);
101 while (!ok && currentPid != 0) {
102 auto current = ProcessInfo::newInstance(currentPid);
103 current->update();
104 currentPid = current->parentPid(&ok);
105 dir = current->currentDir(&ok);
106 }
107
108 return dir;
109}
110
112{
113 bool ok = false;
114
115 QString output(input);
116
117 // search for and replace known marker
118 output.replace(QStringLiteral("%u"), userName());
119 output.replace(QStringLiteral("%h"), localHost());
120 output.replace(QStringLiteral("%n"), name(&ok));
121
122 QString dir = validCurrentDir();
123 if (output.contains(u"%D")) {
124 QString homeDir = userHomeDir();
125 QString tempDir = dir;
126 // Change User's Home Dir w/ ~ only at the beginning
127 if (tempDir.startsWith(homeDir)) {
128 tempDir.remove(0, homeDir.length());
129 tempDir.prepend(u'~');
130 }
131 output.replace(QStringLiteral("%D"), tempDir);
132 }
133 output.replace(QStringLiteral("%d"), formatShortDir(dir));
134
135 return output;
136}
137
138QSet<QString> ProcessInfo::_commonDirNames;
139
140QSet<QString> ProcessInfo::commonDirNames()
141{
142 // static bool forTheFirstTime = true;
143 //
144 // if (forTheFirstTime) {
145 // const KSharedConfigPtr& config = KSharedConfig::openConfig();
146 // const KConfigGroup& configGroup = config->group("ProcessInfo");
147 // _commonDirNames = QSet<QString>::fromList(configGroup.readEntry("CommonDirNames", QStringList()));
148 //
149 // forTheFirstTime = false;
150 // }
151
152 return _commonDirNames;
153}
154
155QString ProcessInfo::formatShortDir(const QString &input) const
156{
157 QString result;
158
159 const QStringList &parts = input.split(QDir::separator());
160
161 QSet<QString> dirNamesToShorten = commonDirNames();
162
163 QListIterator<QString> iter(parts);
164 iter.toBack();
165
166 // go backwards through the list of the path's parts
167 // adding abbreviations of common directory names
168 // and stopping when we reach a dir name which is not
169 // in the commonDirNames set
170 while (iter.hasPrevious()) {
171 const QString &part = iter.previous();
172
173 if (dirNamesToShorten.contains(part)) {
174 result.prepend(QString(QDir::separator()) + part[0]);
175 } else {
176 result.prepend(part);
177 break;
178 }
179 }
180
181 return result;
182}
183
185{
186 *ok = _fields.testFlag(ARGUMENTS);
187
188 return _arguments;
189}
190
192{
193 *ok = _fields.testFlag(ENVIRONMENT);
194
195 return _environment;
196}
197
199{
200 return _fields.testFlag(PROCESS_ID);
201}
202
203int ProcessInfo::pid(bool *ok) const
204{
205 *ok = _fields.testFlag(PROCESS_ID);
206
207 return _pid;
208}
209
210int ProcessInfo::parentPid(bool *ok) const
211{
212 *ok = _fields.testFlag(PARENT_PID);
213
214 return _parentPid;
215}
216
217int ProcessInfo::foregroundPid(bool *ok) const
218{
219 *ok = _fields.testFlag(FOREGROUND_PID);
220
221 return _foregroundPid;
222}
223
225{
226 *ok = _fields.testFlag(NAME);
227
228 return _name;
229}
230
231int ProcessInfo::userId(bool *ok) const
232{
233 *ok = _fields.testFlag(UID);
234
235 return _userId;
236}
237
239{
240 return _userName;
241}
242
244{
245 return _userHomeDir;
246}
247
252
254{
255 _pid = aPid;
256 _fields |= PROCESS_ID;
257}
258
260{
261 _userId = uid;
262 _fields |= UID;
263}
264
266{
267 _userName = name;
269}
270
272{
273 _userHomeDir = QDir::homePath();
274}
275
277{
278 _parentPid = aPid;
279 _fields |= PARENT_PID;
280}
282{
283 _foregroundPid = aPid;
284 _fields |= FOREGROUND_PID;
285}
286
288{
289 if (ok)
290 *ok = _fields & CURRENT_DIR;
291
292 return _currentDir;
293}
295{
296 _fields |= CURRENT_DIR;
297 _currentDir = dir;
298}
299
301{
302 _name = name;
303 _fields |= NAME;
304}
306{
307 _arguments << argument;
308}
309
311{
312 _arguments.clear();
313}
314
316{
317 _environment.insert(name, value);
318}
319
321{
322 switch (error) {
325 break;
326 case QFile::NoError:
328 break;
329 default:
331 }
332}
333
334//
335// ProcessInfo::newInstance() is way at the bottom so it can see all of the
336// implementations of the UnixProcessInfo abstract class.
337//
338
339NullProcessInfo::NullProcessInfo(int aPid, bool enableEnvironmentRead)
340 : ProcessInfo(aPid, enableEnvironmentRead)
341{
342}
343
344bool NullProcessInfo::readProcessInfo(int /*pid*/, bool /*enableEnvironmentRead*/)
345{
346 return false;
347}
348
349void NullProcessInfo::readUserName()
350{
351}
352
353#if !defined(Q_OS_WIN)
354UnixProcessInfo::UnixProcessInfo(int aPid, bool enableEnvironmentRead)
355 : ProcessInfo(aPid, enableEnvironmentRead)
356{
357}
358
359bool UnixProcessInfo::readProcessInfo(int aPid, bool enableEnvironmentRead)
360{
361 // prevent _arguments from growing longer and longer each time this
362 // method is called.
364
365 bool ok = readProcInfo(aPid);
366 if (ok) {
367 ok |= readArguments(aPid);
368 ok |= readCurrentDir(aPid);
369 if (enableEnvironmentRead) {
370 ok |= readEnvironment(aPid);
371 }
372 }
373 return ok;
374}
375
376void UnixProcessInfo::readUserName()
377{
378 bool ok = false;
379 const int uid = userId(&ok);
380 if (!ok)
381 return;
382
383 struct passwd passwdStruct;
384 struct passwd *getpwResult;
385 char *getpwBuffer;
386 long getpwBufferSize;
387 int getpwStatus;
388
389 getpwBufferSize = sysconf(_SC_GETPW_R_SIZE_MAX);
390 if (getpwBufferSize == -1)
391 getpwBufferSize = 16384;
392
393 getpwBuffer = new char[getpwBufferSize];
394 if (getpwBuffer == nullptr)
395 return;
396 getpwStatus = getpwuid_r(uid, &passwdStruct, getpwBuffer, getpwBufferSize, &getpwResult);
397 if ((getpwStatus == 0) && (getpwResult != nullptr)) {
398 setUserName(QString::fromLatin1(passwdStruct.pw_name));
399 } else {
401 qWarning() << "getpwuid_r returned error : " << getpwStatus;
402 }
403 delete[] getpwBuffer;
404}
405#endif
406
407#if defined(Q_OS_LINUX)
408class LinuxProcessInfo : public UnixProcessInfo
409{
410public:
411 LinuxProcessInfo(int aPid, bool env)
412 : UnixProcessInfo(aPid, env)
413 {
414 }
415
416private:
417 bool readProcInfo(int aPid) override
418 {
419 // indicies of various fields within the process status file which
420 // contain various information about the process
421 const int PARENT_PID_FIELD = 3;
422 const int PROCESS_NAME_FIELD = 1;
423 const int GROUP_PROCESS_FIELD = 7;
424
425 QString parentPidString;
426 QString processNameString;
427 QString foregroundPidString;
428 QString uidLine;
429 QString uidString;
430 QStringList uidStrings;
431
432 // For user id read process status file ( /proc/<pid>/status )
433 // Can not use getuid() due to it does not work for 'su'
434 QFile statusInfo(QStringLiteral("/proc/%1/status").arg(aPid));
435 if (statusInfo.open(QIODevice::ReadOnly)) {
436 QTextStream stream(&statusInfo);
437 QString statusLine;
438 do {
439 statusLine = stream.readLine();
440 if (statusLine.startsWith(QLatin1String("Uid:")))
441 uidLine = statusLine;
442 } while (!statusLine.isNull() && uidLine.isNull());
443
444 uidStrings << uidLine.split(u'\t', Qt::SkipEmptyParts);
445 // Must be 5 entries: 'Uid: %d %d %d %d' and
446 // uid string must be less than 5 chars (uint)
447 if (uidStrings.size() == 5)
448 uidString = uidStrings[1];
449 if (uidString.size() > 5)
450 uidString.clear();
451
452 bool ok = false;
453 const int uid = uidString.toInt(&ok);
454 if (ok)
455 setUserId(uid);
456 readUserName();
457 } else {
458 setFileError(statusInfo.error());
459 return false;
460 }
461
462 // read process status file ( /proc/<pid/stat )
463 //
464 // the expected file format is a list of fields separated by spaces, using
465 // parenthesies to escape fields such as the process name which may itself contain
466 // spaces:
467 //
468 // FIELD FIELD (FIELD WITH SPACES) FIELD FIELD
469 //
470 QFile processInfo(QStringLiteral("/proc/%1/stat").arg(aPid));
471 if (processInfo.open(QIODevice::ReadOnly)) {
472 QTextStream stream(&processInfo);
473 const QString &data = stream.readAll();
474
475 int stack = 0;
476 int field = 0;
477 int pos = 0;
478
479 while (pos < data.size()) {
480 QChar c = data[pos];
481
482 if (c == u'(') {
483 stack++;
484 } else if (c == u')') {
485 stack--;
486 } else if (stack == 0 && c == u' ') {
487 field++;
488 } else {
489 switch (field) {
490 case PARENT_PID_FIELD:
491 parentPidString.append(c);
492 break;
493 case PROCESS_NAME_FIELD:
494 processNameString.append(c);
495 break;
496 case GROUP_PROCESS_FIELD:
497 foregroundPidString.append(c);
498 break;
499 }
500 }
501
502 pos++;
503 }
504 } else {
505 setFileError(processInfo.error());
506 return false;
507 }
508
509 // check that data was read successfully
510 bool ok = false;
511 const int foregroundPid = foregroundPidString.toInt(&ok);
512 if (ok)
513 setForegroundPid(foregroundPid);
514
515 const int parentPid = parentPidString.toInt(&ok);
516 if (ok)
517 setParentPid(parentPid);
518
519 if (!processNameString.isEmpty())
520 setName(processNameString);
521
522 // update object state
523 setPid(aPid);
524
525 return ok;
526 }
527
528 bool readArguments(int aPid) override
529 {
530 // read command-line arguments file found at /proc/<pid>/cmdline
531 // the expected format is a list of strings delimited by null characters,
532 // and ending in a double null character pair.
533
534 QFile argumentsFile(QStringLiteral("/proc/%1/cmdline").arg(aPid));
535 if (argumentsFile.open(QIODevice::ReadOnly)) {
536 QTextStream stream(&argumentsFile);
537 const QString &data = stream.readAll();
538
539 const QStringList &argList = data.split(QChar(u'\0'));
540
541 for (const QString &entry : argList) {
542 if (!entry.isEmpty())
543 addArgument(entry);
544 }
545 } else {
546 setFileError(argumentsFile.error());
547 }
548
549 return true;
550 }
551
552 bool readCurrentDir(int aPid) override
553 {
554 char path_buffer[MAXPATHLEN + 1];
555 path_buffer[MAXPATHLEN] = 0;
556 QByteArray procCwd = QFile::encodeName(QStringLiteral("/proc/%1/cwd").arg(aPid));
557 const int length = readlink(procCwd.constData(), path_buffer, MAXPATHLEN);
558 if (length == -1) {
559 setError(UnknownError);
560 return false;
561 }
562
563 path_buffer[length] = '\0';
564 QString path = QFile::decodeName(path_buffer);
565
566 setCurrentDir(path);
567 return true;
568 }
569
570 bool readEnvironment(int aPid) override
571 {
572 // read environment bindings file found at /proc/<pid>/environ
573 // the expected format is a list of KEY=VALUE strings delimited by null
574 // characters and ending in a double null character pair.
575
576 QFile environmentFile(QStringLiteral("/proc/%1/environ").arg(aPid));
577 if (environmentFile.open(QIODevice::ReadOnly)) {
578 QTextStream stream(&environmentFile);
579 const QString &data = stream.readAll();
580
581 const QStringList &bindingList = data.split(QChar(u'\0'));
582
583 for (const QString &entry : bindingList) {
585 QString value;
586
587 const int splitPos = entry.indexOf(u'=');
588
589 if (splitPos != -1) {
590 name = entry.mid(0, splitPos);
591 value = entry.mid(splitPos + 1, -1);
592
593 addEnvironmentBinding(name, value);
594 }
595 }
596 } else {
597 setFileError(environmentFile.error());
598 }
599
600 return true;
601 }
602};
603
604#elif defined(Q_OS_FREEBSD)
605class FreeBSDProcessInfo : public UnixProcessInfo
606{
607public:
608 FreeBSDProcessInfo(int aPid, bool readEnvironment)
609 : UnixProcessInfo(aPid, readEnvironment)
610 {
611 }
612
613private:
614 virtual bool readProcInfo(int aPid)
615 {
616 int managementInfoBase[4];
617 size_t mibLength;
618 struct kinfo_proc *kInfoProc;
619
620 managementInfoBase[0] = CTL_KERN;
621 managementInfoBase[1] = KERN_PROC;
622 managementInfoBase[2] = KERN_PROC_PID;
623 managementInfoBase[3] = aPid;
624
625 if (sysctl(managementInfoBase, 4, NULL, &mibLength, NULL, 0) == -1)
626 return false;
627
628 kInfoProc = new struct kinfo_proc[mibLength];
629
630 if (sysctl(managementInfoBase, 4, kInfoProc, &mibLength, NULL, 0) == -1) {
631 delete[] kInfoProc;
632 return false;
633 }
634
635#if defined(HAVE_OS_DRAGONFLYBSD)
636 setName(QString::fromLatin1(kInfoProc->kp_comm));
637 setPid(kInfoProc->kp_pid);
638 setParentPid(kInfoProc->kp_ppid);
639 setForegroundPid(kInfoProc->kp_pgid);
640 setUserId(kInfoProc->kp_uid);
641#else
642 setName(QString::fromLatin1(kInfoProc->ki_comm));
643 setPid(kInfoProc->ki_pid);
644 setParentPid(kInfoProc->ki_ppid);
645 setForegroundPid(kInfoProc->ki_pgid);
646 setUserId(kInfoProc->ki_uid);
647#endif
648
649 readUserName();
650
651 delete[] kInfoProc;
652 return true;
653 }
654
655 virtual bool readArguments(int aPid)
656 {
657 char args[ARG_MAX];
658 int managementInfoBase[4];
659 size_t len;
660
661 managementInfoBase[0] = CTL_KERN;
662 managementInfoBase[1] = KERN_PROC;
663 managementInfoBase[2] = KERN_PROC_PID;
664 managementInfoBase[3] = aPid;
665
666 len = sizeof(args);
667 if (sysctl(managementInfoBase, 4, args, &len, NULL, 0) == -1)
668 return false;
669
670 const QStringList &argumentList = QString::fromLatin1(args).split(QChar(u'\0'));
671
672 for (QStringList::const_iterator it = argumentList.begin(); it != argumentList.end(); ++it) {
673 addArgument(*it);
674 }
675
676 return true;
677 }
678
679 virtual bool readEnvironment(int aPid)
680 {
681 Q_UNUSED(aPid);
682 // Not supported in FreeBSD?
683 return false;
684 }
685
686 virtual bool readCurrentDir(int aPid)
687 {
688#if defined(HAVE_OS_DRAGONFLYBSD)
689 char buf[PATH_MAX];
690 int managementInfoBase[4];
691 size_t len;
692
693 managementInfoBase[0] = CTL_KERN;
694 managementInfoBase[1] = KERN_PROC;
695 managementInfoBase[2] = KERN_PROC_CWD;
696 managementInfoBase[3] = aPid;
697
698 len = sizeof(buf);
699 if (sysctl(managementInfoBase, 4, buf, &len, NULL, 0) == -1)
700 return false;
701
702 setCurrentDir(buf);
703
704 return true;
705#else
706 int numrecords;
707 struct kinfo_file *info = nullptr;
708
709 info = kinfo_getfile(aPid, &numrecords);
710
711 if (!info)
712 return false;
713
714 for (int i = 0; i < numrecords; ++i) {
715 if (info[i].kf_fd == KF_FD_TYPE_CWD) {
716 setCurrentDir(QString::fromLatin1(info[i].kf_path));
717
718 free(info);
719 return true;
720 }
721 }
722
723 free(info);
724 return false;
725#endif
726 }
727};
728
729#elif defined(Q_OS_OPENBSD)
730class OpenBSDProcessInfo : public UnixProcessInfo
731{
732public:
733 OpenBSDProcessInfo(int aPid, bool readEnvironment)
734 : UnixProcessInfo(aPid, readEnvironment)
735 {
736 }
737
738private:
739 virtual bool readProcInfo(int aPid)
740 {
741 int managementInfoBase[6];
742 size_t mibLength;
743 struct kinfo_proc *kInfoProc;
744
745 managementInfoBase[0] = CTL_KERN;
746 managementInfoBase[1] = KERN_PROC;
747 managementInfoBase[2] = KERN_PROC_PID;
748 managementInfoBase[3] = aPid;
749 managementInfoBase[4] = sizeof(struct kinfo_proc);
750 managementInfoBase[5] = 1;
751
752 if (::sysctl(managementInfoBase, 6, NULL, &mibLength, NULL, 0) == -1) {
753 qWarning() << "first sysctl() call failed with code" << errno;
754 return false;
755 }
756
757 kInfoProc = (struct kinfo_proc *)malloc(mibLength);
758
759 if (::sysctl(managementInfoBase, 6, kInfoProc, &mibLength, NULL, 0) == -1) {
760 qWarning() << "second sysctl() call failed with code" << errno;
761 free(kInfoProc);
762 return false;
763 }
764
765 setName(kInfoProc->p_comm);
766 setPid(kInfoProc->p_pid);
767 setParentPid(kInfoProc->p_ppid);
768 setForegroundPid(kInfoProc->p_tpgid);
769 setUserId(kInfoProc->p_uid);
770 setUserName(kInfoProc->p_login);
771
772 free(kInfoProc);
773 return true;
774 }
775
776 char **readProcArgs(int aPid, int whatMib)
777 {
778 void *buf = NULL;
779 void *nbuf;
780 size_t len = 4096;
781 int rc = -1;
782 int managementInfoBase[4];
783
784 managementInfoBase[0] = CTL_KERN;
785 managementInfoBase[1] = KERN_PROC_ARGS;
786 managementInfoBase[2] = aPid;
787 managementInfoBase[3] = whatMib;
788
789 do {
790 len *= 2;
791 nbuf = realloc(buf, len);
792 if (nbuf == NULL) {
793 break;
794 }
795
796 buf = nbuf;
797 rc = ::sysctl(managementInfoBase, 4, buf, &len, NULL, 0);
798 qWarning() << "sysctl() call failed with code" << errno;
799 } while (rc == -1 && errno == ENOMEM);
800
801 if (nbuf == NULL || rc == -1) {
802 free(buf);
803 return NULL;
804 }
805
806 return (char **)buf;
807 }
808
809 virtual bool readArguments(int aPid)
810 {
811 char **argv;
812
813 argv = readProcArgs(aPid, KERN_PROC_ARGV);
814 if (argv == NULL) {
815 return false;
816 }
817
818 for (char **p = argv; *p != NULL; p++) {
819 addArgument(QString(*p));
820 }
821 free(argv);
822 return true;
823 }
824
825 virtual bool readEnvironment(int aPid)
826 {
827 char **envp;
828 char *eqsign;
829
830 envp = readProcArgs(aPid, KERN_PROC_ENV);
831 if (envp == NULL) {
832 return false;
833 }
834
835 for (char **p = envp; *p != NULL; p++) {
836 eqsign = strchr(*p, '=');
837 if (eqsign == NULL || eqsign[1] == '\0') {
838 continue;
839 }
840 *eqsign = '\0';
841 addEnvironmentBinding(QString((const char *)p), QString((const char *)eqsign + 1));
842 }
843 free(envp);
844 return true;
845 }
846
847 virtual bool readCurrentDir(int aPid)
848 {
849 char buf[PATH_MAX];
850 int managementInfoBase[3];
851 size_t len;
852
853 managementInfoBase[0] = CTL_KERN;
854 managementInfoBase[1] = KERN_PROC_CWD;
855 managementInfoBase[2] = aPid;
856
857 len = sizeof(buf);
858 if (::sysctl(managementInfoBase, 3, buf, &len, NULL, 0) == -1) {
859 qWarning() << "sysctl() call failed with code" << errno;
860 return false;
861 }
862
863 setCurrentDir(buf);
864 return true;
865 }
866};
867
868#elif defined(Q_OS_MAC)
869class MacProcessInfo : public UnixProcessInfo
870{
871public:
872 MacProcessInfo(int aPid, bool env)
873 : UnixProcessInfo(aPid, env)
874 {
875 }
876
877private:
878 virtual bool readProcInfo(int aPid)
879 {
880 int managementInfoBase[4];
881 size_t mibLength;
882 struct kinfo_proc *kInfoProc;
883 /*
884 KDE_struct_stat statInfo;
885
886 // Find the tty device of 'pid' (Example: /dev/ttys001)
887 managementInfoBase[0] = CTL_KERN;
888 managementInfoBase[1] = KERN_PROC;
889 managementInfoBase[2] = KERN_PROC_PID;
890 managementInfoBase[3] = aPid;
891
892 if (sysctl(managementInfoBase, 4, NULL, &mibLength, NULL, 0) == -1) {
893 return false;
894 } else {
895 kInfoProc = new struct kinfo_proc [mibLength];
896 if (sysctl(managementInfoBase, 4, kInfoProc, &mibLength, NULL, 0) == -1) {
897 delete [] kInfoProc;
898 return false;
899 } else {
900 const QString deviceNumber = QString(devname(((&kInfoProc->kp_eproc)->e_tdev), S_IFCHR));
901 const QString fullDeviceName = QString("/dev/") + deviceNumber.rightJustified(3, '0');
902 delete [] kInfoProc;
903
904 const QByteArray deviceName = fullDeviceName.toLatin1();
905 const char* ttyName = deviceName.data();
906
907 if (KDE::stat(ttyName, &statInfo) != 0)
908 return false;
909
910 // Find all processes attached to ttyName
911 managementInfoBase[0] = CTL_KERN;
912 managementInfoBase[1] = KERN_PROC;
913 managementInfoBase[2] = KERN_PROC_TTY;
914 managementInfoBase[3] = statInfo.st_rdev;
915
916 mibLength = 0;
917 if (sysctl(managementInfoBase, sizeof(managementInfoBase) / sizeof(int), NULL, &mibLength, NULL, 0) == -1)
918 return false;
919
920 kInfoProc = new struct kinfo_proc [mibLength];
921 if (sysctl(managementInfoBase, sizeof(managementInfoBase) / sizeof(int), kInfoProc, &mibLength, NULL, 0) == -1)
922 return false;
923
924 // The foreground program is the first one
925 setName(QString(kInfoProc->kp_proc.p_comm));
926
927 delete [] kInfoProc;
928 }
929 setPid(aPid);
930 }
931 return true;
932 */
933 return false;
934 }
935
936 virtual bool readArguments(int aPid)
937 {
938 Q_UNUSED(aPid);
939 return false;
940 }
941 virtual bool readCurrentDir(int aPid)
942 {
943 struct proc_vnodepathinfo vpi;
944 const int nb = proc_pidinfo(aPid, PROC_PIDVNODEPATHINFO, 0, &vpi, sizeof(vpi));
945 if (nb == sizeof(vpi)) {
946 setCurrentDir(QString(vpi.pvi_cdir.vip_path));
947 return true;
948 }
949 return false;
950 }
951 virtual bool readEnvironment(int aPid)
952 {
953 Q_UNUSED(aPid);
954 return false;
955 }
956};
957
958#elif defined(Q_OS_SOLARIS)
959// The procfs structure definition requires off_t to be
960// 32 bits, which only applies if FILE_OFFSET_BITS=32.
961// Futz around here to get it to compile regardless,
962// although some of the structure sizes might be wrong.
963// Fortunately, the structures we actually use don't use
964// off_t, and we're safe.
965#if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64)
966#undef _FILE_OFFSET_BITS
967#endif
968#include <procfs.h>
969
970class SolarisProcessInfo : public UnixProcessInfo
971{
972public:
973 SolarisProcessInfo(int aPid, bool readEnvironment)
974 : UnixProcessInfo(aPid, readEnvironment)
975 {
976 }
977
978private:
979 virtual bool readProcInfo(int aPid)
980 {
981 QFile psinfo(QString("/proc/%1/psinfo").arg(aPid));
982 if (psinfo.open(QIODevice::ReadOnly)) {
983 struct psinfo info;
984 if (psinfo.read((char *)&info, sizeof(info)) != sizeof(info)) {
985 return false;
986 }
987
988 setParentPid(info.pr_ppid);
989 setForegroundPid(info.pr_pgid);
990 setName(info.pr_fname);
991 setPid(aPid);
992
993 // Bogus, because we're treating the arguments as one single string
994 info.pr_psargs[PRARGSZ - 1] = 0;
995 addArgument(info.pr_psargs);
996 }
997 return true;
998 }
999
1000 virtual bool readArguments(int /*pid*/)
1001 {
1002 // Handled in readProcInfo()
1003 return false;
1004 }
1005
1006 virtual bool readEnvironment(int /*pid*/)
1007 {
1008 // Not supported in Solaris
1009 return false;
1010 }
1011
1012 // FIXME: This will have the same issues as BKO 251351; the Linux
1013 // version uses readlink.
1014 virtual bool readCurrentDir(int aPid)
1015 {
1016 QFileInfo info(QString("/proc/%1/path/cwd").arg(aPid));
1017 const bool readable = info.isReadable();
1018
1019 if (readable && info.isSymLink()) {
1020 setCurrentDir(info.symLinkTarget());
1021 return true;
1022 } else {
1023 if (!readable)
1024 setError(PermissionsError);
1025 else
1026 setError(UnknownError);
1027
1028 return false;
1029 }
1030 }
1031};
1032#endif
1033
1035 : _process(process)
1036{
1037 bool ok = false;
1038
1039 // check that this is a SSH process
1040 const QString &name = _process.name(&ok);
1041
1042 if (!ok || name != u"ssh") {
1043 if (!ok)
1044 qWarning() << "Could not read process info";
1045 else
1046 qWarning() << "Process is not a SSH process";
1047
1048 return;
1049 }
1050
1051 // read arguments
1052 const QVector<QString> &args = _process.arguments(&ok);
1053
1054 // SSH options
1055 // these are taken from the SSH manual ( accessed via 'man ssh' )
1056
1057 // options which take no arguments
1058 static const QStringView noArgumentOptions(u"1246AaCfgKkMNnqsTtVvXxYy");
1059 // options which take one argument
1060 static const QStringView singleArgumentOptions(u"bcDeFIiLlmOopRSWw");
1061
1062 if (ok) {
1063 // find the username, host and command arguments
1064 //
1065 // the username/host is assumed to be the first argument
1066 // which is not an option
1067 // ( ie. does not start with a dash '-' character )
1068 // or an argument to a previous option.
1069 //
1070 // the command, if specified, is assumed to be the argument following
1071 // the username and host
1072 //
1073 // note that we skip the argument at index 0 because that is the
1074 // program name ( expected to be 'ssh' in this case )
1075 for (int i = 1; i < args.size(); i++) {
1076 // If this one is an option ...
1077 // Most options together with its argument will be skipped.
1078 if (args[i].startsWith(u'-')) {
1079 const QChar optionChar = (args[i].length() > 1) ? args[i][1] : u'\0';
1080 // for example: -p2222 or -p 2222 ?
1081 const bool optionArgumentCombined = args[i].length() > 2;
1082
1083 if (noArgumentOptions.contains(optionChar)) {
1084 continue;
1085 } else if (singleArgumentOptions.contains(optionChar)) {
1086 QString argument;
1087 if (optionArgumentCombined) {
1088 argument = args[i].mid(2);
1089 } else {
1090 // Verify correct # arguments are given
1091 if ((i + 1) < args.size()) {
1092 argument = args[i + 1];
1093 }
1094 i++;
1095 }
1096
1097 // support using `-l user` to specify username.
1098 if (optionChar == u'l')
1099 _user = argument;
1100 // support using `-p port` to specify port.
1101 else if (optionChar == u'p')
1102 _port = argument;
1103
1104 continue;
1105 }
1106 }
1107
1108 // check whether the host has been found yet
1109 // if not, this must be the username/host argument
1110 if (_host.isEmpty()) {
1111 // check to see if only a hostname is specified, or whether
1112 // both a username and host are specified ( in which case they
1113 // are separated by an '@' character: username@host )
1114
1115 const int separatorPosition = args[i].indexOf(u'@');
1116 if (separatorPosition != -1) {
1117 // username and host specified
1118 _user = args[i].left(separatorPosition);
1119 _host = args[i].mid(separatorPosition + 1);
1120 } else {
1121 // just the host specified
1122 _host = args[i];
1123 }
1124 } else {
1125 // host has already been found, this must be the command argument
1126 _command = args[i];
1127 }
1128 }
1129 } else {
1130 qWarning() << "Could not read arguments";
1131
1132 return;
1133 }
1134}
1135
1137{
1138 return _user;
1139}
1141{
1142 return _host;
1143}
1145{
1146 return _port;
1147}
1149{
1150 return _command;
1151}
1153{
1154 QString output(input);
1155
1156 // test whether host is an ip address
1157 // in which case 'short host' and 'full host'
1158 // markers in the input string are replaced with
1159 // the full address
1160 bool isIpAddress = false;
1161
1162 struct in_addr address;
1163 if (inet_aton(_host.toLocal8Bit().constData(), &address) != 0)
1164 isIpAddress = true;
1165 else
1166 isIpAddress = false;
1167
1168 // search for and replace known markers
1169 output.replace(QStringLiteral("%u"), _user);
1170
1171 if (isIpAddress)
1172 output.replace(QStringLiteral("%h"), _host);
1173 else
1174 output.replace(QStringLiteral("%h"), _host.left(_host.indexOf(u'.')));
1175
1176 output.replace(QStringLiteral("%H"), _host);
1177 output.replace(QStringLiteral("%c"), _command);
1178
1179 return output;
1180}
1181
1182std::unique_ptr<ProcessInfo> ProcessInfo::newInstance(int aPid, bool enableEnvironmentRead)
1183{
1184#if defined(Q_OS_LINUX)
1185 return std::make_unique<LinuxProcessInfo>(aPid, enableEnvironmentRead);
1186#elif defined(Q_OS_SOLARIS)
1187 return std::make_unique<SolarisProcessInfo>(aPid, enableEnvironmentRead);
1188#elif defined(Q_OS_MAC)
1189 return std::make_unique<MacProcessInfo>(aPid, enableEnvironmentRead);
1190#elif defined(Q_OS_FREEBSD)
1191 return std::make_unique<FreeBSDProcessInfo>(aPid, enableEnvironmentRead);
1192#elif defined(Q_OS_OPENBSD)
1193 return std::make_unique<OpenBSDProcessInfo>(aPid, enableEnvironmentRead);
1194#else
1195 return std::make_unique<NullProcessInfo>(aPid, enableEnvironmentRead);
1196#endif
1197}
bool readProcessInfo(int pid, bool readEnvironment) override
This is called on construction to read the process state Subclasses should reimplement this function ...
NullProcessInfo(int pid, bool readEnvironment=false)
Constructs a new NullProcessInfo instance.
Takes a snapshot of the state of a process and provides access to information such as the process nam...
Definition ProcessInfo.h:75
int parentPid(bool *ok) const
Returns the id of the parent process id was read successfully or false otherwise.
void setUserName(const QString &name)
Sets the user name of the process as set by readUserName()
void clearArguments()
clear the commandline arguments for the process, as returned by arguments()
void setUserHomeDir()
Forces the user home directory to be calculated.
ProcessInfo(int pid, bool readEnvironment=false)
Constructs a new process instance.
static QString localHost()
Returns the local host.
void addEnvironmentBinding(const QString &name, const QString &value)
Adds an environment binding for the process, as returned by environment()
void setName(const QString &name)
Sets the name of the process as returned by name()
Error
This enum describes the errors which can occur when trying to read a process's information.
@ PermissionsError
Konsole does not have permission to obtain the process information.
@ UnknownError
The nature of the error is unknown.
@ NoError
No error occurred.
QString currentDir(bool *ok) const
Returns the current working directory of the process.
void setFileError(QFile::FileError error)
Convenience method.
void setParentPid(int pid)
Sets the parent process id as returned by parentPid()
void setPid(int pid)
Sets the process id associated with this ProcessInfo instance.
int pid(bool *ok) const
Returns the process id.
int foregroundPid(bool *ok) const
Returns the id of the current foreground process.
void setError(Error error)
Sets the error.
void setCurrentDir(const QString &dir)
Sets the current working directory for the process.
QMap< QString, QString > environment(bool *ok) const
Returns the environment bindings which the process was started with.
bool isValid() const
Returns true if the process state was read successfully.
QString userHomeDir() const
Returns the user's home directory of the process.
QString format(const QString &text) const
Parses an input string, looking for markers beginning with a '' character and returns a string with t...
void update()
Updates the information about the process.
Error error() const
Returns the last error which occurred.
QString name(bool *ok) const
Returns the name of the current process.
void setUserId(int uid)
Sets the user id associated with this ProcessInfo instance.
QString validCurrentDir() const
Returns the current working directory of the process (or its parent)
void setForegroundPid(int pid)
Sets the foreground process id as returned by foregroundPid()
QVector< QString > arguments(bool *ok) const
Returns the command-line arguments which the process was started with.
QString userName() const
Returns the user's name of the process.
static std::unique_ptr< ProcessInfo > newInstance(int pid, bool readEnvironment=false)
Constructs a new instance of a suitable ProcessInfo sub-class for the current platform which provides...
void addArgument(const QString &argument)
Adds a commandline argument for the process, as returned by arguments()
virtual bool readProcessInfo(int pid, bool readEnvironment)=0
This is called on construction to read the process state Subclasses should reimplement this function ...
QString format(const QString &input) const
Operates in the same way as ProcessInfo::format(), except that the set of markers understood is diffe...
QString userName() const
Returns the user name which the user initially logged into on the remote computer.
QString host() const
Returns the host which the user has connected to.
QString command() const
Returns the command which the user specified to execute on the remote computer when starting the SSH ...
QString port() const
Returns the port on host which the user has connected to.
SSHProcessInfo(const ProcessInfo &process)
Constructs a new SSHProcessInfo instance which provides additional information about the specified SS...
Implementation of ProcessInfo for Unix platforms which uses the /proc filesystem.
UnixProcessInfo(int pid, bool readEnvironment=false)
Constructs a new instance of UnixProcessInfo.
bool readProcessInfo(int pid, bool readEnvironment) override
Implementation of ProcessInfo::readProcessInfo(); calls the four private methods below in turn.
QString name(GameStandardAction id)
QString path(const QString &relativePath)
const char * constData() const const
QString homePath()
QChar separator()
QString decodeName(const QByteArray &localFileName)
QByteArray encodeName(const QString &fileName)
bool testFlag(Enum flag) const const
QString localHostName()
iterator begin()
iterator end()
qsizetype indexOf(const AT &value, qsizetype from) const const
qsizetype length() const const
QList< T > mid(qsizetype pos, qsizetype length) const const
qsizetype size() const const
iterator insert(const Key &key, const T &value)
bool contains(const QSet< T > &other) const const
QString & append(QChar ch)
void clear()
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QString fromLatin1(QByteArrayView str)
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
bool isNull() const const
QString left(qsizetype n) const const
qsizetype length() const const
QString mid(qsizetype position, qsizetype n) const const
QString & prepend(QChar ch)
QString & remove(QChar ch, Qt::CaseSensitivity cs)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
qsizetype size() const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
int toInt(bool *ok, int base) const const
QByteArray toLocal8Bit() const const
bool contains(QChar c, Qt::CaseSensitivity cs) const const
SkipEmptyParts
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Nov 29 2024 11:55:13 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.