Bumping gaia.json for 2 gaia revision(s) a=gaia-bump
[gecko.git] / dom / system / gonk / OpenFileFinder.cpp
blobbaad77806878787a2f2fbb16e2a3a78c74ecdfdf
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "OpenFileFinder.h"
7 #include "mozilla/FileUtils.h"
8 #include "nsPrintfCString.h"
10 #include <sys/stat.h>
11 #include <errno.h>
13 #define USE_DEBUG 0
15 #undef LOG
16 #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "OpenFileFinder", ## args)
17 #define LOGW(args...) __android_log_print(ANDROID_LOG_WARN, "OpenFileFinder", ## args)
18 #define ERR(args...) __android_log_print(ANDROID_LOG_ERROR, "OpenFileFinder", ## args)
20 #if USE_DEBUG
21 #define DBG(args...) __android_log_print(ANDROID_LOG_DEBUG, "OpenFileFinder" , ## args)
22 #else
23 #define DBG(args...)
24 #endif
26 namespace mozilla {
27 namespace system {
29 OpenFileFinder::OpenFileFinder(const nsACString& aPath,
30 bool aCheckIsB2gOrDescendant /* = true */)
31 : mPath(aPath),
32 mProcDir(nullptr),
33 mFdDir(nullptr),
34 mPid(0),
35 mCheckIsB2gOrDescendant(aCheckIsB2gOrDescendant)
37 // We assume that we're running in the parent process
38 mMyPid = getpid();
41 OpenFileFinder::~OpenFileFinder()
43 Close();
46 bool
47 OpenFileFinder::First(OpenFileFinder::Info* aInfo)
49 Close();
51 mProcDir = opendir("/proc");
52 if (!mProcDir) {
53 return false;
55 mState = NEXT_PID;
56 return Next(aInfo);
59 bool
60 OpenFileFinder::Next(OpenFileFinder::Info* aInfo)
62 // NOTE: This function calls readdir and readlink, neither of which should
63 // block since we're using the proc filesystem, which is a purely
64 // kernel in-memory filesystem and doesn't depend on external driver
65 // behaviour.
66 while (mState != DONE) {
67 switch (mState) {
68 case NEXT_PID: {
69 struct dirent *pidEntry;
70 pidEntry = readdir(mProcDir);
71 if (!pidEntry) {
72 mState = DONE;
73 break;
75 char *endPtr;
76 mPid = strtol(pidEntry->d_name, &endPtr, 10);
77 if (mPid == 0 || *endPtr != '\0') {
78 // Not a +ve number - ignore
79 continue;
81 // We've found a /proc/PID directory. Scan open file descriptors.
82 if (mFdDir) {
83 closedir(mFdDir);
85 nsPrintfCString fdDirPath("/proc/%d/fd", mPid);
86 mFdDir = opendir(fdDirPath.get());
87 if (!mFdDir) {
88 continue;
90 mState = CHECK_FDS;
92 // Fall through
93 case CHECK_FDS: {
94 struct dirent *fdEntry;
95 while((fdEntry = readdir(mFdDir))) {
96 if (!strcmp(fdEntry->d_name, ".") ||
97 !strcmp(fdEntry->d_name, "..")) {
98 continue;
100 nsPrintfCString fdSymLink("/proc/%d/fd/%s", mPid, fdEntry->d_name);
101 nsCString resolvedPath;
102 if (ReadSymLink(fdSymLink, resolvedPath) && PathMatches(resolvedPath)) {
103 // We found an open file contained within the directory tree passed
104 // into the constructor.
105 FillInfo(aInfo, resolvedPath);
106 // If sCheckIsB2gOrDescendant is set false, the caller cares about
107 // all processes which have open files. If sCheckIsB2gOrDescendant
108 // is set false, we only care about the b2g proccess or its descendants.
109 if (!mCheckIsB2gOrDescendant || aInfo->mIsB2gOrDescendant) {
110 return true;
112 LOG("Ignore process(%d), not a b2g process or its descendant.",
113 aInfo->mPid);
116 // We've checked all of the files for this pid, move onto the next one.
117 mState = NEXT_PID;
118 continue;
120 case DONE:
121 default:
122 mState = DONE; // covers the default case
123 break;
126 return false;
129 void
130 OpenFileFinder::Close()
132 if (mFdDir) {
133 closedir(mFdDir);
135 if (mProcDir) {
136 closedir(mProcDir);
140 void
141 OpenFileFinder::FillInfo(OpenFileFinder::Info* aInfo, const nsACString& aPath)
143 aInfo->mFileName = aPath;
144 aInfo->mPid = mPid;
145 nsPrintfCString exePath("/proc/%d/exe", mPid);
146 ReadSymLink(exePath, aInfo->mExe);
147 aInfo->mComm.Truncate();
148 aInfo->mAppName.Truncate();
149 nsPrintfCString statPath("/proc/%d/stat", mPid);
150 nsCString statString;
151 statString.SetLength(200);
152 char *stat = statString.BeginWriting();
153 if (!stat) {
154 return;
156 ReadSysFile(statPath.get(), stat, statString.Length());
157 // The stat line includes the comm field, surrounded by parenthesis.
158 // However, the contents of the comm field itself is arbitrary and
159 // and can include ')', so we search for the rightmost ) as being
160 // the end of the comm field.
161 char *closeParen = strrchr(stat, ')');
162 if (!closeParen) {
163 return;
165 char *openParen = strchr(stat, '(');
166 if (!openParen) {
167 return;
169 if (openParen >= closeParen) {
170 return;
172 nsDependentCSubstring comm(&openParen[1], closeParen - openParen - 1);
173 aInfo->mComm = comm;
174 // There is a single character field after the comm and then
175 // the parent pid (the field we're interested in).
176 // ) X ppid
177 // 01234
178 int ppid = atoi(&closeParen[4]);
180 if (mPid == mMyPid) {
181 // This is chrome process
182 aInfo->mIsB2gOrDescendant = true;
183 DBG("Chrome process has open file(s)");
184 return;
186 // For the rest (non-chrome process), we recursively check the ppid to know
187 // it is a descendant of b2g or not. See bug 931456.
188 while (ppid != mMyPid && ppid != 1) {
189 DBG("Process(%d) is not forked from b2g(%d) or Init(1), keep looking",
190 ppid, mMyPid);
191 nsPrintfCString ppStatPath("/proc/%d/stat", ppid);
192 ReadSysFile(ppStatPath.get(), stat, statString.Length());
193 closeParen = strrchr(stat, ')');
194 if (!closeParen) {
195 return;
197 ppid = atoi(&closeParen[4]);
199 if (ppid == 1) {
200 // This is a not a b2g process.
201 DBG("Non-b2g process has open file(s)");
202 aInfo->mIsB2gOrDescendant = false;
203 return;
205 if (ppid == mMyPid) {
206 // This is a descendant of b2g.
207 DBG("Child process of chrome process has open file(s)");
208 aInfo->mIsB2gOrDescendant = true;
211 // This looks like a content process. The comm field will be the
212 // app name.
213 aInfo->mAppName = aInfo->mComm;
216 bool
217 OpenFileFinder::ReadSymLink(const nsACString& aSymLink, nsACString& aOutPath)
219 aOutPath.Truncate();
220 const char *symLink = aSymLink.BeginReading();
222 // Verify that we actually have a symlink.
223 struct stat st;
224 if (lstat(symLink, &st)) {
225 return false;
227 if ((st.st_mode & S_IFMT) != S_IFLNK) {
228 return false;
231 // Contrary to the documentation st.st_size doesn't seem to be a reliable
232 // indication of the length when reading from /proc, so we use a fixed
233 // size buffer instead.
235 char resolvedSymLink[PATH_MAX];
236 ssize_t pathLength = readlink(symLink, resolvedSymLink,
237 sizeof(resolvedSymLink) - 1);
238 if (pathLength <= 0) {
239 return false;
241 resolvedSymLink[pathLength] = '\0';
242 aOutPath.Assign(resolvedSymLink);
243 return true;
246 } // system
247 } // mozilla