diff options
Diffstat (limited to 'dom/system/gonk/OpenFileFinder.cpp')
-rw-r--r-- | dom/system/gonk/OpenFileFinder.cpp | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/dom/system/gonk/OpenFileFinder.cpp b/dom/system/gonk/OpenFileFinder.cpp new file mode 100644 index 000000000..388e813e1 --- /dev/null +++ b/dom/system/gonk/OpenFileFinder.cpp @@ -0,0 +1,251 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "OpenFileFinder.h" + +#include "mozilla/FileUtils.h" +#include "nsPrintfCString.h" + +#include <sys/stat.h> +#include <errno.h> + +#undef USE_DEBUG +#define USE_DEBUG 0 + +#undef LOG +#undef LOGW +#undef ERR +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "OpenFileFinder", ## args) +#define LOGW(args...) __android_log_print(ANDROID_LOG_WARN, "OpenFileFinder", ## args) +#define ERR(args...) __android_log_print(ANDROID_LOG_ERROR, "OpenFileFinder", ## args) + +#undef DBG +#if USE_DEBUG +#define DBG(args...) __android_log_print(ANDROID_LOG_DEBUG, "OpenFileFinder" , ## args) +#else +#define DBG(args...) +#endif + +namespace mozilla { +namespace system { + +OpenFileFinder::OpenFileFinder(const nsACString& aPath, + bool aCheckIsB2gOrDescendant /* = true */) + : mPath(aPath), + mProcDir(nullptr), + mFdDir(nullptr), + mPid(0), + mCheckIsB2gOrDescendant(aCheckIsB2gOrDescendant) +{ + // We assume that we're running in the parent process + mMyPid = getpid(); +} + +OpenFileFinder::~OpenFileFinder() +{ + Close(); +} + +bool +OpenFileFinder::First(OpenFileFinder::Info* aInfo) +{ + Close(); + + mProcDir = opendir("/proc"); + if (!mProcDir) { + return false; + } + mState = NEXT_PID; + return Next(aInfo); +} + +bool +OpenFileFinder::Next(OpenFileFinder::Info* aInfo) +{ + // NOTE: This function calls readdir and readlink, neither of which should + // block since we're using the proc filesystem, which is a purely + // kernel in-memory filesystem and doesn't depend on external driver + // behaviour. + while (mState != DONE) { + switch (mState) { + case NEXT_PID: { + struct dirent *pidEntry; + pidEntry = readdir(mProcDir); + if (!pidEntry) { + mState = DONE; + break; + } + char *endPtr; + mPid = strtol(pidEntry->d_name, &endPtr, 10); + if (mPid == 0 || *endPtr != '\0') { + // Not a +ve number - ignore + continue; + } + // We've found a /proc/PID directory. Scan open file descriptors. + if (mFdDir) { + closedir(mFdDir); + } + nsPrintfCString fdDirPath("/proc/%d/fd", mPid); + mFdDir = opendir(fdDirPath.get()); + if (!mFdDir) { + continue; + } + mState = CHECK_FDS; + } + // Fall through + case CHECK_FDS: { + struct dirent *fdEntry; + while((fdEntry = readdir(mFdDir))) { + if (!strcmp(fdEntry->d_name, ".") || + !strcmp(fdEntry->d_name, "..")) { + continue; + } + nsPrintfCString fdSymLink("/proc/%d/fd/%s", mPid, fdEntry->d_name); + nsCString resolvedPath; + if (ReadSymLink(fdSymLink, resolvedPath) && PathMatches(resolvedPath)) { + // We found an open file contained within the directory tree passed + // into the constructor. + FillInfo(aInfo, resolvedPath); + // If sCheckIsB2gOrDescendant is set false, the caller cares about + // all processes which have open files. If sCheckIsB2gOrDescendant + // is set false, we only care about the b2g proccess or its descendants. + if (!mCheckIsB2gOrDescendant || aInfo->mIsB2gOrDescendant) { + return true; + } + LOG("Ignore process(%d), not a b2g process or its descendant.", + aInfo->mPid); + } + } + // We've checked all of the files for this pid, move onto the next one. + mState = NEXT_PID; + continue; + } + case DONE: + default: + mState = DONE; // covers the default case + break; + } + } + return false; +} + +void +OpenFileFinder::Close() +{ + if (mFdDir) { + closedir(mFdDir); + } + if (mProcDir) { + closedir(mProcDir); + } +} + +void +OpenFileFinder::FillInfo(OpenFileFinder::Info* aInfo, const nsACString& aPath) +{ + aInfo->mFileName = aPath; + aInfo->mPid = mPid; + nsPrintfCString exePath("/proc/%d/exe", mPid); + ReadSymLink(exePath, aInfo->mExe); + aInfo->mComm.Truncate(); + aInfo->mAppName.Truncate(); + nsPrintfCString statPath("/proc/%d/stat", mPid); + nsCString statString; + statString.SetLength(200); + char *stat = statString.BeginWriting(); + if (!stat) { + return; + } + ReadSysFile(statPath.get(), stat, statString.Length()); + // The stat line includes the comm field, surrounded by parenthesis. + // However, the contents of the comm field itself is arbitrary and + // and can include ')', so we search for the rightmost ) as being + // the end of the comm field. + char *closeParen = strrchr(stat, ')'); + if (!closeParen) { + return; + } + char *openParen = strchr(stat, '('); + if (!openParen) { + return; + } + if (openParen >= closeParen) { + return; + } + nsDependentCSubstring comm(&openParen[1], closeParen - openParen - 1); + aInfo->mComm = comm; + // There is a single character field after the comm and then + // the parent pid (the field we're interested in). + // ) X ppid + // 01234 + int ppid = atoi(&closeParen[4]); + + if (mPid == mMyPid) { + // This is chrome process + aInfo->mIsB2gOrDescendant = true; + DBG("Chrome process has open file(s)"); + return; + } + // For the rest (non-chrome process), we recursively check the ppid to know + // it is a descendant of b2g or not. See bug 931456. + while (ppid != mMyPid && ppid != 1) { + DBG("Process(%d) is not forked from b2g(%d) or Init(1), keep looking", + ppid, mMyPid); + nsPrintfCString ppStatPath("/proc/%d/stat", ppid); + ReadSysFile(ppStatPath.get(), stat, statString.Length()); + closeParen = strrchr(stat, ')'); + if (!closeParen) { + return; + } + ppid = atoi(&closeParen[4]); + } + if (ppid == 1) { + // This is a not a b2g process. + DBG("Non-b2g process has open file(s)"); + aInfo->mIsB2gOrDescendant = false; + return; + } + if (ppid == mMyPid) { + // This is a descendant of b2g. + DBG("Child process of chrome process has open file(s)"); + aInfo->mIsB2gOrDescendant = true; + } + + // This looks like a content process. The comm field will be the + // app name. + aInfo->mAppName = aInfo->mComm; +} + +bool +OpenFileFinder::ReadSymLink(const nsACString& aSymLink, nsACString& aOutPath) +{ + aOutPath.Truncate(); + const char *symLink = aSymLink.BeginReading(); + + // Verify that we actually have a symlink. + struct stat st; + if (lstat(symLink, &st)) { + return false; + } + if ((st.st_mode & S_IFMT) != S_IFLNK) { + return false; + } + + // Contrary to the documentation st.st_size doesn't seem to be a reliable + // indication of the length when reading from /proc, so we use a fixed + // size buffer instead. + + char resolvedSymLink[PATH_MAX]; + ssize_t pathLength = readlink(symLink, resolvedSymLink, + sizeof(resolvedSymLink) - 1); + if (pathLength <= 0) { + return false; + } + resolvedSymLink[pathLength] = '\0'; + aOutPath.Assign(resolvedSymLink); + return true; +} + +} // system +} // mozilla |