File indexing completed on 2024-04-28 05:31:39
0001 /* 0002 SPDX-FileCopyrightText: 2007 John Tapsell <tapsell@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "process.h" 0008 #include "processes_local_p.h" 0009 #include "read_procsmaps_runnable.h" 0010 0011 #include <klocalizedstring.h> 0012 0013 #include <QByteArray> 0014 #include <QDir> 0015 #include <QFile> 0016 #include <QHash> 0017 #include <QSet> 0018 #include <QTextStream> 0019 #include <QThreadPool> 0020 0021 // for sysconf 0022 #include <unistd.h> 0023 // for kill and setNice 0024 #include <dirent.h> 0025 #include <errno.h> 0026 #include <signal.h> 0027 #include <stdlib.h> 0028 #include <sys/resource.h> 0029 #include <sys/types.h> 0030 // for ionice 0031 #include <asm/unistd.h> 0032 #include <sys/ptrace.h> 0033 // for getsched 0034 #include <sched.h> 0035 0036 #define PROCESS_BUFFER_SIZE 1000 0037 0038 /* For ionice */ 0039 extern int sys_ioprio_set(int, int, int); 0040 extern int sys_ioprio_get(int, int); 0041 0042 #define HAVE_IONICE 0043 /* Check if this system has ionice */ 0044 #if !defined(SYS_ioprio_get) || !defined(SYS_ioprio_set) 0045 /* All new kernels have SYS_ioprio_get and _set defined, but for the few that do not, here are the definitions */ 0046 #if defined(__i386__) 0047 #define __NR_ioprio_set 289 0048 #define __NR_ioprio_get 290 0049 #elif defined(__ppc__) || defined(__powerpc__) 0050 #define __NR_ioprio_set 273 0051 #define __NR_ioprio_get 274 0052 #elif defined(__x86_64__) 0053 #define __NR_ioprio_set 251 0054 #define __NR_ioprio_get 252 0055 #elif defined(__ia64__) 0056 #define __NR_ioprio_set 1274 0057 #define __NR_ioprio_get 1275 0058 #else 0059 #ifdef __GNUC__ 0060 #warning "This architecture does not support IONICE. Disabling ionice feature." 0061 #endif 0062 #undef HAVE_IONICE 0063 #endif 0064 /* Map these to SYS_ioprio_get */ 0065 #define SYS_ioprio_get __NR_ioprio_get 0066 #define SYS_ioprio_set __NR_ioprio_set 0067 0068 #endif /* !SYS_ioprio_get */ 0069 0070 /* Set up ionice functions */ 0071 #ifdef HAVE_IONICE 0072 #define IOPRIO_WHO_PROCESS 1 0073 #define IOPRIO_CLASS_SHIFT 13 0074 0075 /* Expose the kernel calls to userspace via syscall 0076 * See man ioprio_set and man ioprio_get for information on these functions */ 0077 static int ioprio_set(int which, int who, int ioprio) 0078 { 0079 return syscall(SYS_ioprio_set, which, who, ioprio); 0080 } 0081 0082 static int ioprio_get(int which, int who) 0083 { 0084 return syscall(SYS_ioprio_get, which, who); 0085 } 0086 #endif 0087 0088 namespace KSysGuard 0089 { 0090 class ProcessesLocal::Private 0091 { 0092 public: 0093 Private() 0094 { 0095 mProcDir = opendir("/proc"); 0096 } 0097 ~Private(); 0098 inline bool readProcStatus(const QString &dir, Process *process); 0099 inline bool readProcStat(const QString &dir, Process *process); 0100 inline bool readProcStatm(const QString &dir, Process *process); 0101 inline bool readProcCmdline(const QString &dir, Process *process); 0102 inline bool readProcCGroup(const QString &dir, Process *process); 0103 inline bool readProcAttr(const QString &dir, Process *process); 0104 inline bool getNiceness(long pid, Process *process); 0105 inline bool getIOStatistics(const QString &dir, Process *process); 0106 QFile mFile; 0107 char mBuffer[PROCESS_BUFFER_SIZE + 1]; // used as a buffer to read data into 0108 DIR *mProcDir; 0109 }; 0110 0111 ProcessesLocal::Private::~Private() 0112 { 0113 closedir(mProcDir); 0114 } 0115 0116 ProcessesLocal::ProcessesLocal() 0117 : d(new Private()) 0118 { 0119 } 0120 bool ProcessesLocal::Private::readProcStatus(const QString &dir, Process *process) 0121 { 0122 mFile.setFileName(dir + QStringLiteral("status")); 0123 if (!mFile.open(QIODevice::ReadOnly)) 0124 return false; /* process has terminated in the meantime */ 0125 0126 process->setUid(0); 0127 process->setGid(0); 0128 process->setTracerpid(-1); 0129 process->setNumThreads(0); 0130 process->setNoNewPrivileges(0); 0131 0132 int size; 0133 int found = 0; // count how many fields we found 0134 while ((size = mFile.readLine(mBuffer, sizeof(mBuffer))) > 0) { //-1 indicates an error 0135 switch (mBuffer[0]) { 0136 case 'N': 0137 if ((unsigned int)size > sizeof("Name:") && qstrncmp(mBuffer, "Name:", sizeof("Name:") - 1) == 0) { 0138 if (process->command().isEmpty()) { 0139 process->setName(QString::fromLocal8Bit(mBuffer + sizeof("Name:") - 1, size - sizeof("Name:") + 1).trimmed()); 0140 } 0141 if (++found == 6) { 0142 goto finish; 0143 } 0144 } else if ((unsigned int)size > sizeof("NoNewPrivs:") && qstrncmp(mBuffer, "NoNewPrivs:", sizeof("NoNewPrivs:") - 1) == 0) { 0145 process->setNoNewPrivileges(atol(mBuffer + sizeof("NoNewPrivs:") - 1)); 0146 if (++found == 6) { 0147 goto finish; 0148 } 0149 } 0150 break; 0151 case 'U': 0152 if ((unsigned int)size > sizeof("Uid:") && qstrncmp(mBuffer, "Uid:", sizeof("Uid:") - 1) == 0) { 0153 qlonglong uid; 0154 qlonglong euid; 0155 qlonglong suid; 0156 qlonglong fsuid; 0157 sscanf(mBuffer + sizeof("Uid:") - 1, "%lld %lld %lld %lld", &uid, &euid, &suid, &fsuid); 0158 process->setUid(uid); 0159 process->setEuid(euid); 0160 process->setSuid(suid); 0161 process->setFsuid(fsuid); 0162 if (++found == 6) { 0163 goto finish; 0164 } 0165 } 0166 break; 0167 case 'G': 0168 if ((unsigned int)size > sizeof("Gid:") && qstrncmp(mBuffer, "Gid:", sizeof("Gid:") - 1) == 0) { 0169 qlonglong gid, egid, sgid, fsgid; 0170 sscanf(mBuffer + sizeof("Gid:") - 1, "%lld %lld %lld %lld", &gid, &egid, &sgid, &fsgid); 0171 process->setGid(gid); 0172 process->setEgid(egid); 0173 process->setSgid(sgid); 0174 process->setFsgid(fsgid); 0175 if (++found == 6) { 0176 goto finish; 0177 } 0178 } 0179 break; 0180 case 'T': 0181 if ((unsigned int)size > sizeof("TracerPid:") && qstrncmp(mBuffer, "TracerPid:", sizeof("TracerPid:") - 1) == 0) { 0182 process->setTracerpid(atol(mBuffer + sizeof("TracerPid:") - 1)); 0183 if (process->tracerpid() == 0) { 0184 process->setTracerpid(-1); 0185 } 0186 if (++found == 6) { 0187 goto finish; 0188 } 0189 } else if ((unsigned int)size > sizeof("Threads:") && qstrncmp(mBuffer, "Threads:", sizeof("Threads:") - 1) == 0) { 0190 process->setNumThreads(atol(mBuffer + sizeof("Threads:") - 1)); 0191 if (++found == 6) { 0192 goto finish; 0193 } 0194 } 0195 break; 0196 default: 0197 break; 0198 } 0199 } 0200 0201 finish: 0202 mFile.close(); 0203 return true; 0204 } 0205 0206 bool ProcessesLocal::Private::readProcCGroup(const QString &dir, Process *process) 0207 { 0208 mFile.setFileName(dir + QStringLiteral("cgroup")); 0209 if (!mFile.open(QIODevice::ReadOnly)) { 0210 return false; /* process has terminated in the meantime */ 0211 } 0212 0213 while (mFile.readLine(mBuffer, sizeof(mBuffer)) > 0) { //-1 indicates an error 0214 if (mBuffer[0] == '0' && mBuffer[1] == ':' && mBuffer[2] == ':') { 0215 process->setCGroup(QString::fromLocal8Bit(&mBuffer[3]).trimmed()); 0216 break; 0217 } 0218 } 0219 mFile.close(); 0220 return true; 0221 } 0222 0223 bool ProcessesLocal::Private::readProcAttr(const QString &dir, Process *process) 0224 { 0225 mFile.setFileName(dir + QStringLiteral("attr/current")); 0226 if (!mFile.open(QIODevice::ReadOnly)) { 0227 return false; /* process has terminated in the meantime */ 0228 } 0229 0230 if (mFile.readLine(mBuffer, sizeof(mBuffer)) > 0) { //-1 indicates an error 0231 process->setMACContext(QString::fromLocal8Bit(mBuffer).trimmed()); 0232 } 0233 mFile.close(); 0234 return true; 0235 } 0236 0237 long ProcessesLocal::getParentPid(long pid) 0238 { 0239 if (pid <= 0) { 0240 return -1; 0241 } 0242 d->mFile.setFileName(QStringLiteral("/proc/") + QString::number(pid) + QStringLiteral("/stat")); 0243 if (!d->mFile.open(QIODevice::ReadOnly)) { 0244 return -1; /* process has terminated in the meantime */ 0245 } 0246 0247 int size; // amount of data read in 0248 if ((size = d->mFile.readLine(d->mBuffer, sizeof(d->mBuffer))) <= 0) { //-1 indicates nothing read 0249 d->mFile.close(); 0250 return -1; 0251 } 0252 0253 d->mFile.close(); 0254 char *word = d->mBuffer; 0255 // The command name is the second parameter, and this ends with a closing bracket. So find the last 0256 // closing bracket and start from there 0257 word = strrchr(word, ')'); 0258 if (!word) { 0259 return -1; 0260 } 0261 word++; // Nove to the space after the last ")" 0262 int current_word = 1; 0263 0264 while (true) { 0265 if (word[0] == ' ') { 0266 if (++current_word == 3) { 0267 break; 0268 } 0269 } else if (word[0] == 0) { 0270 return -1; // end of data - serious problem 0271 } 0272 word++; 0273 } 0274 long ppid = atol(++word); 0275 if (ppid == 0) { 0276 return -1; 0277 } 0278 return ppid; 0279 } 0280 0281 bool ProcessesLocal::Private::readProcStat(const QString &dir, Process *ps) 0282 { 0283 QString filename = dir + QStringLiteral("stat"); 0284 // As an optimization, if the last file read in was stat, then we already have this info in memory 0285 if (mFile.fileName() != filename) { 0286 mFile.setFileName(filename); 0287 if (!mFile.open(QIODevice::ReadOnly)) { 0288 return false; /* process has terminated in the meantime */ 0289 } 0290 if (mFile.readLine(mBuffer, sizeof(mBuffer)) <= 0) { //-1 indicates nothing read 0291 mFile.close(); 0292 return false; 0293 } 0294 mFile.close(); 0295 } 0296 0297 char *word = mBuffer; 0298 // The command name is the second parameter, and this ends with a closing bracket. So find the last 0299 // closing bracket and start from there 0300 word = strrchr(word, ')'); 0301 if (!word) { 0302 return false; 0303 } 0304 word++; // Nove to the space after the last ")" 0305 int current_word = 1; // We've skipped the process ID and now at the end of the command name 0306 char status = '\0'; 0307 unsigned long long vmSize = 0; 0308 unsigned long long vmRSS = 0; 0309 while (current_word < 23) { 0310 if (word[0] == ' ') { 0311 ++current_word; 0312 switch (current_word) { 0313 case 2: // status 0314 status = word[1]; // Look at the first letter of the status. 0315 // We analyze this after the while loop 0316 break; 0317 case 6: // ttyNo 0318 { 0319 int ttyNo = atoi(word + 1); 0320 int major = ttyNo >> 8; 0321 int minor = ttyNo & 0xff; 0322 switch (major) { 0323 case 136: 0324 ps->setTty(QByteArray("pts/") + QByteArray::number(minor)); 0325 break; 0326 case 5: 0327 ps->setTty(QByteArray("tty")); 0328 break; 0329 case 4: 0330 if (minor < 64) { 0331 ps->setTty(QByteArray("tty") + QByteArray::number(minor)); 0332 } else { 0333 ps->setTty(QByteArray("ttyS") + QByteArray::number(minor - 64)); 0334 } 0335 break; 0336 default: 0337 ps->setTty(QByteArray()); 0338 } 0339 } break; 0340 case 13: // userTime 0341 ps->setUserTime(atoll(word + 1)); 0342 break; 0343 case 14: // sysTime 0344 ps->setSysTime(atoll(word + 1)); 0345 break; 0346 case 18: // niceLevel 0347 ps->setNiceLevel(atoi(word + 1)); /*Or should we use getPriority instead? */ 0348 break; 0349 case 21: // startTime 0350 ps->setStartTime(atoll(word + 1)); 0351 break; 0352 case 22: // vmSize 0353 vmSize = atoll(word + 1); 0354 break; 0355 case 23: // vmRSS 0356 vmRSS = atoll(word + 1); 0357 break; 0358 default: 0359 break; 0360 } 0361 } else if (word[0] == 0) { 0362 return false; // end of data - serious problem 0363 } 0364 word++; 0365 } 0366 0367 /* There was a "(ps->vmRss+3) * sysconf(_SC_PAGESIZE)" here in the original ksysguard code. I have no idea why! After comparing it to 0368 * meminfo and other tools, this means we report the RSS by 12 bytes differently compared to them. So I'm removing the +3 0369 * to be consistent. NEXT TIME COMMENT STRANGE THINGS LIKE THAT! :-) 0370 * 0371 * Update: I think I now know why - the kernel allocates 3 pages for 0372 * tracking information about each the process. This memory isn't 0373 * included in vmRSS..*/ 0374 ps->setVmRSS(vmRSS * (sysconf(_SC_PAGESIZE) / 1024)); /*convert to KiB*/ 0375 ps->setVmSize(vmSize / 1024); /* convert to KiB */ 0376 0377 switch (status) { 0378 case 'R': 0379 ps->setStatus(Process::Running); 0380 break; 0381 case 'S': 0382 ps->setStatus(Process::Sleeping); 0383 break; 0384 case 'D': 0385 ps->setStatus(Process::DiskSleep); 0386 break; 0387 case 'Z': 0388 ps->setStatus(Process::Zombie); 0389 break; 0390 case 'T': 0391 ps->setStatus(Process::Stopped); 0392 break; 0393 case 'W': 0394 ps->setStatus(Process::Paging); 0395 break; 0396 default: 0397 ps->setStatus(Process::OtherStatus); 0398 break; 0399 } 0400 return true; 0401 } 0402 0403 bool ProcessesLocal::Private::readProcStatm(const QString &dir, Process *process) 0404 { 0405 #ifdef _SC_PAGESIZE 0406 mFile.setFileName(dir + QStringLiteral("statm")); 0407 if (!mFile.open(QIODevice::ReadOnly)) { 0408 return false; /* process has terminated in the meantime */ 0409 } 0410 0411 if (mFile.readLine(mBuffer, sizeof(mBuffer)) <= 0) { //-1 indicates nothing read 0412 mFile.close(); 0413 return 0; 0414 } 0415 mFile.close(); 0416 0417 int current_word = 0; 0418 char *word = mBuffer; 0419 0420 while (true) { 0421 if (word[0] == ' ') { 0422 if (++current_word == 2) { 0423 // number of pages that are shared 0424 break; 0425 } 0426 } else if (word[0] == 0) { 0427 return false; // end of data - serious problem 0428 } 0429 word++; 0430 } 0431 long shared = atol(word + 1); 0432 0433 /* we use the rss - shared to find the amount of memory just this app uses */ 0434 process->setVmURSS(process->vmRSS() - (shared * sysconf(_SC_PAGESIZE) / 1024)); 0435 #else 0436 process->setVmURSS(0); 0437 #endif 0438 return true; 0439 } 0440 0441 bool ProcessesLocal::Private::readProcCmdline(const QString &dir, Process *process) 0442 { 0443 if (!process->command().isNull()) { 0444 return true; // only parse the cmdline once. This function takes up 25% of the CPU time :-/ 0445 } 0446 mFile.setFileName(dir + QStringLiteral("cmdline")); 0447 if (!mFile.open(QIODevice::ReadOnly)) { 0448 return false; /* process has terminated in the meantime */ 0449 } 0450 0451 QTextStream in(&mFile); 0452 process->setCommand(in.readAll()); 0453 0454 // cmdline separates parameters with the NULL character 0455 if (!process->command().isEmpty()) { 0456 // extract non-truncated name from cmdline 0457 int zeroIndex = process->command().indexOf(QLatin1Char('\0')); 0458 int processNameStart = process->command().lastIndexOf(QLatin1Char('/'), zeroIndex); 0459 if (processNameStart == -1) { 0460 processNameStart = 0; 0461 } else { 0462 processNameStart++; 0463 } 0464 QString nameFromCmdLine = process->command().mid(processNameStart, zeroIndex - processNameStart); 0465 if (nameFromCmdLine.startsWith(process->name())) { 0466 process->setName(nameFromCmdLine); 0467 } 0468 0469 process->command().replace(QLatin1Char('\0'), QLatin1Char(' ')); 0470 } 0471 0472 mFile.close(); 0473 return true; 0474 } 0475 0476 bool ProcessesLocal::Private::getNiceness(long pid, Process *process) 0477 { 0478 int sched = sched_getscheduler(pid); 0479 switch (sched) { 0480 case (SCHED_OTHER): 0481 process->setScheduler(KSysGuard::Process::Other); 0482 break; 0483 case (SCHED_RR): 0484 process->setScheduler(KSysGuard::Process::RoundRobin); 0485 break; 0486 case (SCHED_FIFO): 0487 process->setScheduler(KSysGuard::Process::Fifo); 0488 break; 0489 #ifdef SCHED_IDLE 0490 case (SCHED_IDLE): 0491 process->setScheduler(KSysGuard::Process::SchedulerIdle); 0492 break; 0493 #endif 0494 #ifdef SCHED_BATCH 0495 case (SCHED_BATCH): 0496 process->setScheduler(KSysGuard::Process::Batch); 0497 break; 0498 #endif 0499 default: 0500 process->setScheduler(KSysGuard::Process::Other); 0501 } 0502 if (sched == SCHED_FIFO || sched == SCHED_RR) { 0503 struct sched_param param; 0504 if (sched_getparam(pid, ¶m) == 0) { 0505 process->setNiceLevel(param.sched_priority); 0506 } else { 0507 process->setNiceLevel(0); // Error getting scheduler parameters. 0508 } 0509 } 0510 0511 #ifdef HAVE_IONICE 0512 int ioprio = ioprio_get(IOPRIO_WHO_PROCESS, pid); /* Returns from 0 to 7 for the iopriority, and -1 if there's an error */ 0513 if (ioprio == -1) { 0514 process->setIoniceLevel(-1); 0515 process->setIoPriorityClass(KSysGuard::Process::None); 0516 return false; /* Error. Just give up. */ 0517 } 0518 process->setIoniceLevel(ioprio & 0xff); /* Bottom few bits are the priority */ 0519 process->setIoPriorityClass((KSysGuard::Process::IoPriorityClass)(ioprio >> IOPRIO_CLASS_SHIFT)); /* Top few bits are the class */ 0520 return true; 0521 #else 0522 return false; /* Do nothing, if we do not support this architecture */ 0523 #endif 0524 } 0525 0526 bool ProcessesLocal::Private::getIOStatistics(const QString &dir, Process *process) 0527 { 0528 QString filename = dir + QStringLiteral("io"); 0529 // As an optimization, if the last file read in was io, then we already have this info in memory 0530 mFile.setFileName(filename); 0531 if (!mFile.open(QIODevice::ReadOnly)) { 0532 return false; /* process has terminated in the meantime */ 0533 } 0534 if (mFile.read(mBuffer, sizeof(mBuffer)) <= 0) { //-1 indicates nothing read 0535 mFile.close(); 0536 return false; 0537 } 0538 mFile.close(); 0539 0540 int current_word = 0; // count from 0 0541 char *word = mBuffer; 0542 while (current_word < 6 && word[0] != 0) { 0543 if (word[0] == ' ') { 0544 qlonglong number = atoll(word + 1); 0545 switch (current_word++) { 0546 case 0: // rchar - characters read 0547 process->setIoCharactersRead(number); 0548 break; 0549 case 1: // wchar - characters written 0550 process->setIoCharactersWritten(number); 0551 break; 0552 case 2: // syscr - read syscall 0553 process->setIoReadSyscalls(number); 0554 break; 0555 case 3: // syscw - write syscall 0556 process->setIoWriteSyscalls(number); 0557 break; 0558 case 4: // read_bytes - bytes actually read from I/O 0559 process->setIoCharactersActuallyRead(number); 0560 break; 0561 case 5: // write_bytes - bytes actually written to I/O 0562 process->setIoCharactersActuallyWritten(number); 0563 default: 0564 break; 0565 } 0566 } 0567 word++; 0568 } 0569 return true; 0570 } 0571 0572 bool ProcessesLocal::updateProcessInfo(long pid, Process *process) 0573 { 0574 bool success = true; 0575 const QString dir = QLatin1String("/proc/") + QString::number(pid) + QLatin1Char('/'); 0576 0577 if (mUpdateFlags.testFlag(Processes::Smaps)) { 0578 auto runnable = new ReadProcSmapsRunnable{dir}; 0579 0580 connect(runnable, &ReadProcSmapsRunnable::finished, this, [this, pid](qulonglong pss) { 0581 Q_EMIT processUpdated(pid, {{Process::VmPSS, pss}}); 0582 }); 0583 0584 QThreadPool::globalInstance()->start(runnable); 0585 } 0586 0587 if (!d->readProcStat(dir, process)) { 0588 success = false; 0589 } 0590 if (!d->readProcStatus(dir, process)) { 0591 success = false; 0592 } 0593 if (!d->readProcStatm(dir, process)) { 0594 success = false; 0595 } 0596 if (!d->readProcCmdline(dir, process)) { 0597 success = false; 0598 } 0599 if (!d->readProcCGroup(dir, process)) { 0600 success = false; 0601 } 0602 if (!d->readProcAttr(dir, process)) { 0603 success = false; 0604 } 0605 if (!d->getNiceness(pid, process)) { 0606 success = false; 0607 } 0608 if (mUpdateFlags.testFlag(Processes::IOStatistics) && !d->getIOStatistics(dir, process)) { 0609 success = false; 0610 } 0611 0612 return success; 0613 } 0614 0615 QSet<long> ProcessesLocal::getAllPids() 0616 { 0617 QSet<long> pids; 0618 if (d->mProcDir == nullptr) { 0619 return pids; // There's not much we can do without /proc 0620 } 0621 struct dirent *entry; 0622 rewinddir(d->mProcDir); 0623 while ((entry = readdir(d->mProcDir))) { 0624 if (entry->d_name[0] >= '0' && entry->d_name[0] <= '9') { 0625 pids.insert(atol(entry->d_name)); 0626 } 0627 } 0628 return pids; 0629 } 0630 0631 Processes::Error ProcessesLocal::sendSignal(long pid, int sig) 0632 { 0633 errno = 0; 0634 if (pid <= 0) { 0635 return Processes::InvalidPid; 0636 } 0637 if (kill((pid_t)pid, sig)) { 0638 switch (errno) { 0639 case ESRCH: 0640 return Processes::ProcessDoesNotExistOrZombie; 0641 case EINVAL: 0642 return Processes::InvalidParameter; 0643 case EPERM: 0644 return Processes::InsufficientPermissions; 0645 } 0646 // Kill failed 0647 return Processes::Unknown; 0648 } 0649 return Processes::NoError; 0650 } 0651 0652 Processes::Error ProcessesLocal::setNiceness(long pid, int priority) 0653 { 0654 errno = 0; 0655 if (pid <= 0) { 0656 return Processes::InvalidPid; 0657 } 0658 auto error = [] { 0659 switch (errno) { 0660 case ESRCH: 0661 case ENOENT: 0662 return Processes::ProcessDoesNotExistOrZombie; 0663 case EINVAL: 0664 return Processes::InvalidParameter; 0665 case EACCES: 0666 case EPERM: 0667 return Processes::InsufficientPermissions; 0668 default: 0669 return Processes::Unknown; 0670 } 0671 }; 0672 auto threadList{QDir(QString::fromLatin1("/proc/%1/task").arg(pid)).entryList(QDir::NoDotAndDotDot | QDir::Dirs)}; 0673 if (threadList.isEmpty()) { 0674 return error(); 0675 } 0676 for (auto entry : threadList) { 0677 int threadId = entry.toInt(); 0678 if (!threadId) { 0679 return Processes::InvalidParameter; 0680 } 0681 if (setpriority(PRIO_PROCESS, threadId, priority)) { 0682 return error(); 0683 } 0684 } 0685 return Processes::NoError; 0686 } 0687 0688 Processes::Error ProcessesLocal::setScheduler(long pid, int priorityClass, int priority) 0689 { 0690 errno = 0; 0691 if (priorityClass == KSysGuard::Process::Other || priorityClass == KSysGuard::Process::Batch || priorityClass == KSysGuard::Process::SchedulerIdle) { 0692 priority = 0; 0693 } 0694 if (pid <= 0) { 0695 return Processes::InvalidPid; 0696 } 0697 struct sched_param params; 0698 params.sched_priority = priority; 0699 int policy; 0700 switch (priorityClass) { 0701 case (KSysGuard::Process::Other): 0702 policy = SCHED_OTHER; 0703 break; 0704 case (KSysGuard::Process::RoundRobin): 0705 policy = SCHED_RR; 0706 break; 0707 case (KSysGuard::Process::Fifo): 0708 policy = SCHED_FIFO; 0709 break; 0710 #ifdef SCHED_IDLE 0711 case (KSysGuard::Process::SchedulerIdle): 0712 policy = SCHED_IDLE; 0713 break; 0714 #endif 0715 #ifdef SCHED_BATCH 0716 case (KSysGuard::Process::Batch): 0717 policy = SCHED_BATCH; 0718 break; 0719 #endif 0720 default: 0721 return Processes::NotSupported; 0722 } 0723 0724 auto error = [] { 0725 switch (errno) { 0726 case ESRCH: 0727 case ENOENT: 0728 return Processes::ProcessDoesNotExistOrZombie; 0729 case EINVAL: 0730 return Processes::InvalidParameter; 0731 case EPERM: 0732 return Processes::InsufficientPermissions; 0733 default: 0734 return Processes::Unknown; 0735 } 0736 }; 0737 auto threadList{QDir(QString::fromLatin1("/proc/%1/task").arg(pid)).entryList(QDir::NoDotAndDotDot | QDir::Dirs)}; 0738 if (threadList.isEmpty()) { 0739 return error(); 0740 } 0741 for (auto entry : threadList) { 0742 int threadId = entry.toInt(); 0743 if (!threadId) { 0744 return Processes::InvalidParameter; 0745 } 0746 if (sched_setscheduler(threadId, policy, ¶ms) != 0) { 0747 return error(); 0748 } 0749 } 0750 return Processes::NoError; 0751 } 0752 0753 Processes::Error ProcessesLocal::setIoNiceness(long pid, int priorityClass, int priority) 0754 { 0755 errno = 0; 0756 if (pid <= 0) { 0757 return Processes::InvalidPid; 0758 } 0759 #ifdef HAVE_IONICE 0760 if (ioprio_set(IOPRIO_WHO_PROCESS, pid, priority | priorityClass << IOPRIO_CLASS_SHIFT) == -1) { 0761 // set io niceness failed 0762 switch (errno) { 0763 case ESRCH: 0764 return Processes::ProcessDoesNotExistOrZombie; 0765 break; 0766 case EINVAL: 0767 return Processes::InvalidParameter; 0768 case EPERM: 0769 return Processes::InsufficientPermissions; 0770 } 0771 return Processes::Unknown; 0772 } 0773 return Processes::NoError; 0774 #else 0775 return Processes::NotSupported; 0776 #endif 0777 } 0778 0779 bool ProcessesLocal::supportsIoNiceness() 0780 { 0781 #ifdef HAVE_IONICE 0782 return true; 0783 #else 0784 return false; 0785 #endif 0786 } 0787 0788 long long ProcessesLocal::totalPhysicalMemory() 0789 { 0790 // Try to get the memory via sysconf. Note the cast to long long to try to avoid a long overflow 0791 // Should we use sysconf(_SC_PAGESIZE) or getpagesize() ? 0792 #ifdef _SC_PHYS_PAGES 0793 return ((long long)sysconf(_SC_PHYS_PAGES)) * (sysconf(_SC_PAGESIZE) / 1024); 0794 #else 0795 // This is backup code in case this is not defined. It should never fail on a linux system. 0796 0797 d->mFile.setFileName("/proc/meminfo"); 0798 if (!d->mFile.open(QIODevice::ReadOnly)) { 0799 return 0; 0800 } 0801 0802 int size; 0803 while ((size = d->mFile.readLine(d->mBuffer, sizeof(d->mBuffer))) > 0) { //-1 indicates an error 0804 switch (d->mBuffer[0]) { 0805 case 'M': 0806 if ((unsigned int)size > sizeof("MemTotal:") && qstrncmp(d->mBuffer, "MemTotal:", sizeof("MemTotal:") - 1) == 0) { 0807 d->mFile.close(); 0808 return atoll(d->mBuffer + sizeof("MemTotal:") - 1); 0809 } 0810 } 0811 } 0812 return 0; // Not found. Probably will never happen 0813 #endif 0814 } 0815 ProcessesLocal::~ProcessesLocal() 0816 { 0817 delete d; 0818 } 0819 0820 }