diff --git a/processcore/process.h b/processcore/process.h --- a/processcore/process.h +++ b/processcore/process.h @@ -184,6 +184,9 @@ int numThreads() const; ///< Number of threads that this process has, including the main one. 0 if not known void setNumThreads(int number); ///< The number of threads that this process has, including this process. + int noNewPrivileges() const; + void setNoNewPrivileges(int number); ///< Linux process flag NoNewPrivileges + int index() const; ///< Each process has a parent process. Each sibling has a unique number to identify it under that parent. This is that number. void setIndex(int index); @@ -209,6 +212,9 @@ QString schedulerAsString() const; ///< Returns a translated string of the scheduler class. e.g. "FIFO", "Round Robin", "Batch" + QString cGroup() const; + void setCGroup(const QString &cGroup); ///< Linux Control Group (cgroup) + /** This is the number of 1/1000ths of a second since this * particular process was last updated compared to when all the processes * were updated. The purpose is to allow a more fine tracking of the time diff --git a/processcore/process.cpp b/processcore/process.cpp --- a/processcore/process.cpp +++ b/processcore/process.cpp @@ -82,6 +82,8 @@ int index; Process::Changes changes; int elapsedTimeMilliSeconds; + int noNewPrivileges; + QString cGroup; }; Process::Process() @@ -291,6 +293,11 @@ return d->startTime; } +int Process::noNewPrivileges() const +{ + return d->noNewPrivileges; +} + int Process::userUsage() const { return d->userUsage; @@ -481,6 +488,11 @@ return d->elapsedTimeMilliSeconds; } +QString Process::cGroup() const +{ + return d->cGroup; +} + void Process::setParentPid(long int parent_pid) { d->parent_pid = parent_pid; @@ -582,6 +594,12 @@ d->startTime = startTime; } +void Process::setNoNewPrivileges(int number) { + if(d->noNewPrivileges == number) return; + d->noNewPrivileges = number; + d->changes |= Process::Status; +} + void Process::setUserUsage(int _userUsage) { if(d->userUsage == _userUsage) return; d->userUsage = _userUsage; @@ -765,4 +783,10 @@ d->changes = changes; } +void Process::setCGroup(const QString &_cGroup) { + if(d->cGroup == _cGroup) return; + d->cGroup = _cGroup; + d->changes |= Process::Status; +} + } diff --git a/processcore/processes_linux_p.cpp b/processcore/processes_linux_p.cpp --- a/processcore/processes_linux_p.cpp +++ b/processcore/processes_linux_p.cpp @@ -112,6 +112,7 @@ inline bool readProcStat(const QString &dir, Process *process); inline bool readProcStatm(const QString &dir, Process *process); inline bool readProcCmdline(const QString &dir, Process *process); + inline bool readProcCGroup(const QString &dir, Process *process); inline bool getNiceness(long pid, Process *process); inline bool getIOStatistics(const QString &dir, Process *process); QFile mFile; @@ -138,6 +139,7 @@ process->setGid(0); process->setTracerpid(-1); process->setNumThreads(0); + process->setNoNewPrivileges(0); int size; int found = 0; //count how many fields we found @@ -147,7 +149,10 @@ if((unsigned int)size > sizeof("Name:") && qstrncmp(mBuffer, "Name:", sizeof("Name:")-1) == 0) { if(process->command().isEmpty()) process->setName(QString::fromLocal8Bit(mBuffer + sizeof("Name:")-1, size-sizeof("Name:")+1).trimmed()); - if(++found == 5) goto finish; + if(++found == 6) goto finish; + } else if((unsigned int)size > sizeof("NoNewPrivs:") && qstrncmp(mBuffer, "NoNewPrivs:", sizeof("NoNewPrivs:")-1) == 0) { + process->setNoNewPrivileges(atol(mBuffer + sizeof("NoNewPrivs:")-1)); + if(++found == 6) goto finish; } break; case 'U': @@ -161,7 +166,7 @@ process->setEuid(euid); process->setSuid(suid); process->setFsuid(fsuid); - if(++found == 5) goto finish; + if(++found == 6) goto finish; } break; case 'G': @@ -172,18 +177,18 @@ process->setEgid(egid); process->setSgid(sgid); process->setFsgid(fsgid); - if(++found == 5) goto finish; + if(++found == 6) goto finish; } break; case 'T': if((unsigned int)size > sizeof("TracerPid:") && qstrncmp(mBuffer, "TracerPid:", sizeof("TracerPid:")-1) == 0) { process->setTracerpid(atol(mBuffer + sizeof("TracerPid:")-1)); if (process->tracerpid() == 0) process->setTracerpid(-1); - if(++found == 5) goto finish; + if(++found == 6) goto finish; } else if((unsigned int)size > sizeof("Threads:") && qstrncmp(mBuffer, "Threads:", sizeof("Threads:")-1) == 0) { process->setNumThreads(atol(mBuffer + sizeof("Threads:")-1)); - if(++found == 5) goto finish; + if(++found == 6) goto finish; } break; default: @@ -196,6 +201,22 @@ return true; } +bool ProcessesLocal::Private::readProcCGroup(const QString &dir, Process *process) +{ + mFile.setFileName(dir + QStringLiteral("cgroup")); + if(!mFile.open(QIODevice::ReadOnly)) + return false; /* process has terminated in the meantime */ + + while( mFile.readLine( mBuffer, sizeof(mBuffer)) > 0) { //-1 indicates an error + if ( mBuffer[0] == '0' && mBuffer[1] == ':' && mBuffer[2] == ':' ) { + process->setCGroup(QString::fromLocal8Bit(&mBuffer[3]).trimmed()); + break; + } + } + mFile.close(); + return true; +} + long ProcessesLocal::getParentPid(long pid) { if (pid <= 0) return -1; @@ -520,6 +541,7 @@ if(!d->readProcStatus(dir, process)) success = false; if(!d->readProcStatm(dir, process)) success = false; if(!d->readProcCmdline(dir, process)) success = false; + if(!d->readProcCGroup(dir, process)) success = false; if(!d->getNiceness(pid, process)) success = false; if(mUpdateFlags.testFlag(Processes::IOStatistics) && !d->getIOStatistics(dir, process)) success = false; diff --git a/processcore/processes_remote_p.cpp b/processcore/processes_remote_p.cpp --- a/processcore/processes_remote_p.cpp +++ b/processcore/processes_remote_p.cpp @@ -42,7 +42,7 @@ statusColumn = userColumn = systemColumn = niceColumn = vmSizeColumn = vmRSSColumn = loginColumn = commandColumn = tracerPidColumn = ttyColumn = ioprioClassColumn = ioprioColumn = - vmURSSColumn = -1; + vmURSSColumn = noNewPrivilegesColumn = cGroupColumn = -1; usedMemory = freeMemory;} ~Private() {} QString host; @@ -69,6 +69,8 @@ int ioprioClassColumn; int ioprioColumn; int ttyColumn; + int noNewPrivilegesColumn; + int cGroupColumn; int numColumns; @@ -137,6 +139,8 @@ if(d->ttyColumn!= -1) process->setTty(p.at(d->ttyColumn)); if(d->ioprioColumn!= -1) process->setIoniceLevel(p.at(d->ioprioColumn).toInt()); if(d->ioprioClassColumn!= -1) process->setIoPriorityClass((KSysGuard::Process::IoPriorityClass)(p.at(d->ioprioClassColumn).toInt())); + if(d->noNewPrivilegesColumn!= -1) process->setNoNewPrivileges(p.at(d->noNewPrivilegesColumn).toLong()); + if(d->nameColumn!= -1) process->setCGroup(QString::fromUtf8(p.at(d->cGroupColumn))); return true; } @@ -242,6 +246,10 @@ d->ioprioClassColumn = i; else if(info[i] == "IO Priority") d->ioprioColumn = i; + else if(info[i] == "NNP") + d->noNewPrivilegesColumn = i; + else if(info[i] == "CGroup") + d->cGroupColumn = i; } d->havePsInfo = true; break; diff --git a/processui/ProcessModel.h b/processui/ProcessModel.h --- a/processui/ProcessModel.h +++ b/processui/ProcessModel.h @@ -138,7 +138,7 @@ * setup header function, and make sure you increase PROCESSHEADERVERSION. This will ensure * that old saved settings won't be used */ -#define PROCESSHEADERVERSION 6 +#define PROCESSHEADERVERSION 8 enum { HeadingName=0, HeadingUser, HeadingPid, @@ -152,9 +152,11 @@ HeadingMemory, HeadingSharedMemory, HeadingStartTime, + HeadingNoNewPrivileges, HeadingCommand, HeadingXMemory, - HeadingXTitle + HeadingXTitle, + HeadingCGroup }; enum { UidRole = Qt::UserRole, SortingValueRole, WindowIdRole, PlainValueRole, PercentageRole, PercentageHistoryRole }; diff --git a/processui/ProcessModel.cpp b/processui/ProcessModel.cpp --- a/processui/ProcessModel.cpp +++ b/processui/ProcessModel.cpp @@ -262,6 +262,8 @@ case HeadingStartTime: { return processLeft->startTime() > processRight->startTime(); } + case HeadingNoNewPrivileges: + return processLeft->noNewPrivileges() > processRight->noNewPrivileges(); case HeadingXMemory: return processLeft->pixmapBytes() > processRight->pixmapBytes(); case HeadingVmSize: @@ -756,6 +758,13 @@ index = q->createIndex(row, ProcessModel::HeadingUser, process); emit q->dataChanged(index, index); } + if(process->changes() & KSysGuard::Process::Status) { + totalUpdated+=2; + QModelIndex index = q->createIndex(row, ProcessModel::HeadingNoNewPrivileges, process); + emit q->dataChanged(index, index); + index = q->createIndex(row, ProcessModel::HeadingCGroup, process); + emit q->dataChanged(index, index); + } if(process->changes() & KSysGuard::Process::NiceLevels) { totalUpdated++; QModelIndex index = q->createIndex(row, ProcessModel::HeadingNiceness, process); @@ -961,6 +970,7 @@ case HeadingXMemory: case HeadingSharedMemory: case HeadingStartTime: + case HeadingNoNewPrivileges: case HeadingIoRead: case HeadingIoWrite: case HeadingVmSize: @@ -1007,6 +1017,8 @@ return i18n("This is approximately the amount of real physical memory that this process's shared libraries are using.
This memory is shared among all processes that use this library.
"); case HeadingStartTime: return i18n("The elapsed time since the process was started."); + case HeadingNoNewPrivileges: + return i18n("Linux flag NoNewPrivileges, if set the process can't gain further privileges via setuid etc."); case HeadingCommand: return i18n("The command with which this process was launched."); case HeadingXMemory: @@ -1019,6 +1031,8 @@ return i18n("The number of bytes read. See What's This for more information."); case HeadingIoWrite: return i18n("The number of bytes written. See What's This for more information."); + case HeadingCGroup: + return i18n("The control group (cgroup) where this process belongs."); default: return QVariant(); } @@ -1050,6 +1064,8 @@ return i18n("Technical information: This is an approximation of the Shared memory, called SHR in top. It is the number of pages that are backed by a file (see kernel Documentation/filesystems/proc.txt). For an individual process, see \"Detailed Memory Information\" for a more accurate, but slower, calculation of the true Shared memory usage."); case HeadingStartTime: return i18n("Technical information: The underlying value (clock ticks since system boot) is retrieved from /proc/[pid]/stat"); + case HeadingNoNewPrivileges: + return i18n("Technical information: The flag is retrieved from /proc/[pid]/status"); case HeadingCommand: return i18n("Technical information: This is from /proc/*/cmdline"); case HeadingXMemory: @@ -1071,6 +1087,8 @@ "

" "The number in brackets shows the rate at which each value is changing, determined from taking the difference between the previous value and the new value, and dividing by the update interval.

" "Technical information: This data is collected from /proc/*/io and is documented further in Documentation/accounting and Documentation/filesystems/proc.txt in the kernel source."); + case HeadingCGroup: + return i18n("Technical information: This shows Linux Control Group (cgroup) membership, retrieved from /proc/[pid]/cgroup. Control groups are used by Systemd and containers for limiting process group's usage of resources and to monitor them."); default: return QVariant(); } @@ -1316,6 +1334,8 @@ const auto relativeStartTime = absoluteStartTime.secsTo(QDateTime::currentDateTime()); return TimeUtil::secondsToHumanElapsedString(relativeStartTime); } + case HeadingNoNewPrivileges: + return QString::number(process->noNewPrivileges()); case HeadingCommand: { return process->command().replace(QLatin1Char('\n'),QLatin1Char(' ')); @@ -1391,6 +1411,8 @@ return w->name; } #endif + case HeadingCGroup: + return process->cGroup(); default: return QVariant(); } @@ -1659,6 +1681,7 @@ case HeadingNiceness: case HeadingCPUTime: case HeadingStartTime: + case HeadingNoNewPrivileges: case HeadingPid: case HeadingMemory: case HeadingXMemory: @@ -1720,6 +1743,8 @@ return (qlonglong)(process->vmRSS() - process->vmURSS()); case HeadingStartTime: return process->startTime(); // 2015-01-03, gregormi: can maybe be replaced with something better later + case HeadingNoNewPrivileges: + return process->noNewPrivileges(); case HeadingCommand: return process->command(); case HeadingIoRead: @@ -1767,6 +1792,8 @@ return w->name; } #endif + case HeadingCGroup: + return process->cGroup(); default: return QVariant(); } @@ -1942,13 +1969,15 @@ headings << i18nc("process heading", "Memory"); headings << i18nc("process heading", "Shared Mem"); headings << i18nc("process heading", "Relative Start Time"); + headings << i18nc("process heading", "NNP"); headings << i18nc("process heading", "Command"); #if HAVE_X11 if (d->mIsX11) { headings << i18nc("process heading", "X11 Memory"); headings << i18nc("process heading", "Window Title"); } #endif + headings << i18nc("process heading", "CGroup"); if(d->mHeadings.isEmpty()) { // If it's empty, this is the first time this has been called, so insert the headings beginInsertColumns(QModelIndex(), 0, headings.count()-1);