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