// ---------------------------------------------------------------------- // File: ProcessInfo.cc // Author: Georgios Bitzes - CERN // ---------------------------------------------------------------------- /************************************************************************ * EOS - the CERN Disk Storage System * * Copyright (C) 2011 CERN/Switzerland * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* ************************************************************************/ #define __STDC_FORMAT_MACROS #include #include #include "ProcessInfo.hh" #include "common/Logging.hh" bool ProcessInfoProvider::fromString(const std::string& procstat, const std::string& cmdline, ProcessInfo& ret) { if (!ret.isEmpty()) { THROW("The ProcessInfo object must be empty for this function to fill!"); } if (!parseStat(procstat, ret)) { return false; } parseCmdline(cmdline, ret); return true; } // Reference: // Table 1-4: Contents of the stat files (as of 2.6.30-rc7) // .............................................................................. // Field Content // pid process id // tcomm filename of the executable // state state (R is running, S is sleeping, D is sleeping in an // uninterruptible wait, Z is zombie, T is traced or stopped) // ppid process id of the parent process // pgrp pgrp of the process // sid session id // tty_nr tty the process uses // tty_pgrp pgrp of the tty // flags task flags // min_flt number of minor faults // cmin_flt number of minor faults with child's // maj_flt number of major faults // cmaj_flt number of major faults with child's // utime user mode jiffies // stime kernel mode jiffies // cutime user mode jiffies with child's // cstime kernel mode jiffies with child's // priority priority level // nice nice level // num_threads number of threads // it_real_value (obsolete, always 0) // start_time time the process started after system boot // vsize virtual memory size // rss resident set memory size // rsslim current limit in bytes on the rss // start_code address above which program text can run // end_code address below which program text can run // start_stack address of the start of the main process stack // esp current value of ESP // eip current value of EIP // pending bitmap of pending signals // blocked bitmap of blocked signals // sigign bitmap of ignored signals // sigcatch bitmap of caught signals // 0 (place holder, used to be the wchan address, use /proc/PID/wchan instead) // 0 (place holder) // 0 (place holder) // exit_signal signal to send to parent thread on exit // task_cpu which CPU the task is scheduled on // rt_priority realtime priority // policy scheduling policy (man sched_setscheduler) // blkio_ticks time spent waiting for block IO // gtime guest time of the task in jiffies // cgtime guest time of the task children in jiffies // start_data address above which program data+bss is placed // end_data address below which program data+bss is placed // start_brk address above which program heap can be expanded with brk() // arg_start address above which program command line is placed // arg_end address below which program command line is placed // env_start address above which program environment is placed // env_end address below which program environment is placed // exit_code the thread's exit_code in the form reported by the waitpid system call // .............................................................................. bool ProcessInfoProvider::parseStat(const std::string& procstat, ProcessInfo& ret) { if (!ret.isEmpty()) { THROW("The ProcessInfo object must be empty for this function to fill!"); } // variables to assist with parsing bool inParenth = false; size_t tokenCount = 0; bool success = false; // variables in which to store results pid_t pid; pid_t ppid; pid_t pgrp; pid_t sid; Jiffies startTime; unsigned flags; // let's parse for (size_t i = 0; i < procstat.size(); i++) { // be careful, process names can have all kinds of combinations of () in it ! // we will fail parsing if a process ends with a name like ') ' where = R|S|Z|D|T and there is a space of if (procstat[i] == '(') { inParenth = true; continue; } if (procstat[i] == ')' && procstat[i + 1] == ' ' && procstat[i + 3] == ' ' && ((procstat[i + 2] == 'R') || (procstat[i + 2] == 'S') || (procstat[i + 2] == 'Z') || (procstat[i + 2] == 'D') || (procstat[i + 2] == 'T'))) { if (!inParenth) { return false; // parse error } inParenth = false; } // start of a token, use scanf if we're interested in it if (!inParenth && (procstat[i] == ' ' || i == 0)) { switch (tokenCount) { case 0: { if (!sscanf(procstat.c_str() + i, "%u", &pid)) { return false; } break; } case 3: { if (!sscanf(procstat.c_str() + i, "%u", &ppid)) { return false; } break; } case 4: { if (!sscanf(procstat.c_str() + i, "%u", &pgrp)) { return false; } break; } case 5: { if (!sscanf(procstat.c_str() + i, "%u", &sid)) { return false; } break; } case 8: { if (!sscanf(procstat.c_str() + i, "%u", &flags)) { return false; } break; } case 21: { if (!sscanf(procstat.c_str() + i, "%" PRId64, &startTime)) { return false; } success = true; break; } } tokenCount++; } } if (!success) { return false; } ret.fillStat(pid, ppid, pgrp, sid, startTime, flags); return true; } void ProcessInfoProvider::parseCmdline(const std::string& cmdline, ProcessInfo& ret) { if (cmdline.empty()) { return; } ret.fillCmdline(split_on_nullbyte(cmdline)); } void ProcessInfoProvider::inject(pid_t pid, const ProcessInfo& info) { std::lock_guard lock(mtx); useInjectedData = true; injections[pid] = info; } bool ProcessInfoProvider::retrieveBasic(pid_t pid, ProcessInfo& ret) { if (useInjectedData) { std::lock_guard lock(mtx); auto it = injections.find(pid); if (it == injections.end()) { return false; } ret = it->second; // Keep the same behavior as when reading from /proc, don't give out the // cmdline even if the injection contains it ret.fillCmdline({}); return true; } std::string procstat; if (!readFile(SSTR("/proc/" << pid << "/stat"), procstat)) { return false; } if (!parseStat(procstat, ret)) { return false; } if (pid != ret.getPid()) { eos_static_crit("Hell has frozen over, /proc/%d/stat contained information for a different pid: %d", pid, ret.getPid()); return false; } return true; } bool ProcessInfoProvider::retrieveFull(pid_t pid, ProcessInfo& ret) { if (useInjectedData) { std::lock_guard lock(mtx); auto it = injections.find(pid); if (it == injections.end()) { return false; } ret = it->second; ret.fillRmInfo(); return true; } if (!retrieveBasic(pid, ret)) { return false; } std::string cmdline; if (!readFile(SSTR("/proc/" << pid << "/cmdline"), cmdline)) { // This is a valid case, if for example, the calling PID is actually // a kernel thread. return true; } parseCmdline(cmdline, ret); parseExec(pid, ret); ret.fillRmInfo(); // Read path of /proc//exe std::string exePath = SSTR("/proc/" << pid << "/exe"); char buffer[1024]; ssize_t outcome = readlink(exePath.c_str(), buffer, 1024); if (outcome > 0) { ret.exe = std::string(buffer, outcome); } return true; } bool ProcessInfoProvider::parseExec(pid_t pid, ProcessInfo& ret) { const size_t BUFF_SIZE = 8096; char buffer[BUFF_SIZE]; ssize_t len = readlink(SSTR("/proc/" << pid << "/exe").c_str(), buffer, BUFF_SIZE - 2); if (len == -1) { return false; } ret.fillExecutablePath(std::string(buffer, len)); return true; }