summaryrefslogtreecommitdiffstats
path: root/dom/system/gonk/OpenFileFinder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/system/gonk/OpenFileFinder.cpp')
-rw-r--r--dom/system/gonk/OpenFileFinder.cpp251
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