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"
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)
21 #define DBG(args...) __android_log_print(ANDROID_LOG_DEBUG, "OpenFileFinder" , ## args)
29 OpenFileFinder::OpenFileFinder(const nsACString
& aPath
,
30 bool aCheckIsB2gOrDescendant
/* = true */)
35 mCheckIsB2gOrDescendant(aCheckIsB2gOrDescendant
)
37 // We assume that we're running in the parent process
41 OpenFileFinder::~OpenFileFinder()
47 OpenFileFinder::First(OpenFileFinder::Info
* aInfo
)
51 mProcDir
= opendir("/proc");
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
66 while (mState
!= DONE
) {
69 struct dirent
*pidEntry
;
70 pidEntry
= readdir(mProcDir
);
76 mPid
= strtol(pidEntry
->d_name
, &endPtr
, 10);
77 if (mPid
== 0 || *endPtr
!= '\0') {
78 // Not a +ve number - ignore
81 // We've found a /proc/PID directory. Scan open file descriptors.
85 nsPrintfCString
fdDirPath("/proc/%d/fd", mPid
);
86 mFdDir
= opendir(fdDirPath
.get());
94 struct dirent
*fdEntry
;
95 while((fdEntry
= readdir(mFdDir
))) {
96 if (!strcmp(fdEntry
->d_name
, ".") ||
97 !strcmp(fdEntry
->d_name
, "..")) {
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
) {
112 LOG("Ignore process(%d), not a b2g process or its descendant.",
116 // We've checked all of the files for this pid, move onto the next one.
122 mState
= DONE
; // covers the default case
130 OpenFileFinder::Close()
141 OpenFileFinder::FillInfo(OpenFileFinder::Info
* aInfo
, const nsACString
& aPath
)
143 aInfo
->mFileName
= aPath
;
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();
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
, ')');
165 char *openParen
= strchr(stat
, '(');
169 if (openParen
>= closeParen
) {
172 nsDependentCSubstring
comm(&openParen
[1], closeParen
- openParen
- 1);
174 // There is a single character field after the comm and then
175 // the parent pid (the field we're interested in).
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)");
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",
191 nsPrintfCString
ppStatPath("/proc/%d/stat", ppid
);
192 ReadSysFile(ppStatPath
.get(), stat
, statString
.Length());
193 closeParen
= strrchr(stat
, ')');
197 ppid
= atoi(&closeParen
[4]);
200 // This is a not a b2g process.
201 DBG("Non-b2g process has open file(s)");
202 aInfo
->mIsB2gOrDescendant
= false;
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
213 aInfo
->mAppName
= aInfo
->mComm
;
217 OpenFileFinder::ReadSymLink(const nsACString
& aSymLink
, nsACString
& aOutPath
)
220 const char *symLink
= aSymLink
.BeginReading();
222 // Verify that we actually have a symlink.
224 if (lstat(symLink
, &st
)) {
227 if ((st
.st_mode
& S_IFMT
) != S_IFLNK
) {
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) {
241 resolvedSymLink
[pathLength
] = '\0';
242 aOutPath
.Assign(resolvedSymLink
);