1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/system/gonk/OpenFileFinder.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,247 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "OpenFileFinder.h" 1.9 + 1.10 +#include "mozilla/FileUtils.h" 1.11 +#include "nsPrintfCString.h" 1.12 + 1.13 +#include <sys/stat.h> 1.14 +#include <errno.h> 1.15 + 1.16 +#define USE_DEBUG 0 1.17 + 1.18 +#undef LOG 1.19 +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "OpenFileFinder", ## args) 1.20 +#define LOGW(args...) __android_log_print(ANDROID_LOG_WARN, "OpenFileFinder", ## args) 1.21 +#define ERR(args...) __android_log_print(ANDROID_LOG_ERROR, "OpenFileFinder", ## args) 1.22 + 1.23 +#if USE_DEBUG 1.24 +#define DBG(args...) __android_log_print(ANDROID_LOG_DEBUG, "OpenFileFinder" , ## args) 1.25 +#else 1.26 +#define DBG(args...) 1.27 +#endif 1.28 + 1.29 +namespace mozilla { 1.30 +namespace system { 1.31 + 1.32 +OpenFileFinder::OpenFileFinder(const nsACString& aPath, 1.33 + bool aCheckIsB2gOrDescendant /* = true */) 1.34 + : mPath(aPath), 1.35 + mProcDir(nullptr), 1.36 + mFdDir(nullptr), 1.37 + mPid(0), 1.38 + mCheckIsB2gOrDescendant(aCheckIsB2gOrDescendant) 1.39 +{ 1.40 + // We assume that we're running in the parent process 1.41 + mMyPid = getpid(); 1.42 +} 1.43 + 1.44 +OpenFileFinder::~OpenFileFinder() 1.45 +{ 1.46 + Close(); 1.47 +} 1.48 + 1.49 +bool 1.50 +OpenFileFinder::First(OpenFileFinder::Info* aInfo) 1.51 +{ 1.52 + Close(); 1.53 + 1.54 + mProcDir = opendir("/proc"); 1.55 + if (!mProcDir) { 1.56 + return false; 1.57 + } 1.58 + mState = NEXT_PID; 1.59 + return Next(aInfo); 1.60 +} 1.61 + 1.62 +bool 1.63 +OpenFileFinder::Next(OpenFileFinder::Info* aInfo) 1.64 +{ 1.65 + // NOTE: This function calls readdir and readlink, neither of which should 1.66 + // block since we're using the proc filesystem, which is a purely 1.67 + // kernel in-memory filesystem and doesn't depend on external driver 1.68 + // behaviour. 1.69 + while (mState != DONE) { 1.70 + switch (mState) { 1.71 + case NEXT_PID: { 1.72 + struct dirent *pidEntry; 1.73 + pidEntry = readdir(mProcDir); 1.74 + if (!pidEntry) { 1.75 + mState = DONE; 1.76 + break; 1.77 + } 1.78 + char *endPtr; 1.79 + mPid = strtol(pidEntry->d_name, &endPtr, 10); 1.80 + if (mPid == 0 || *endPtr != '\0') { 1.81 + // Not a +ve number - ignore 1.82 + continue; 1.83 + } 1.84 + // We've found a /proc/PID directory. Scan open file descriptors. 1.85 + if (mFdDir) { 1.86 + closedir(mFdDir); 1.87 + } 1.88 + nsPrintfCString fdDirPath("/proc/%d/fd", mPid); 1.89 + mFdDir = opendir(fdDirPath.get()); 1.90 + if (!mFdDir) { 1.91 + continue; 1.92 + } 1.93 + mState = CHECK_FDS; 1.94 + } 1.95 + // Fall through 1.96 + case CHECK_FDS: { 1.97 + struct dirent *fdEntry; 1.98 + while((fdEntry = readdir(mFdDir))) { 1.99 + if (!strcmp(fdEntry->d_name, ".") || 1.100 + !strcmp(fdEntry->d_name, "..")) { 1.101 + continue; 1.102 + } 1.103 + nsPrintfCString fdSymLink("/proc/%d/fd/%s", mPid, fdEntry->d_name); 1.104 + nsCString resolvedPath; 1.105 + if (ReadSymLink(fdSymLink, resolvedPath) && PathMatches(resolvedPath)) { 1.106 + // We found an open file contained within the directory tree passed 1.107 + // into the constructor. 1.108 + FillInfo(aInfo, resolvedPath); 1.109 + // If sCheckIsB2gOrDescendant is set false, the caller cares about 1.110 + // all processes which have open files. If sCheckIsB2gOrDescendant 1.111 + // is set false, we only care about the b2g proccess or its descendants. 1.112 + if (!mCheckIsB2gOrDescendant || aInfo->mIsB2gOrDescendant) { 1.113 + return true; 1.114 + } 1.115 + LOG("Ignore process(%d), not a b2g process or its descendant.", 1.116 + aInfo->mPid); 1.117 + } 1.118 + } 1.119 + // We've checked all of the files for this pid, move onto the next one. 1.120 + mState = NEXT_PID; 1.121 + continue; 1.122 + } 1.123 + case DONE: 1.124 + default: 1.125 + mState = DONE; // covers the default case 1.126 + break; 1.127 + } 1.128 + } 1.129 + return false; 1.130 +} 1.131 + 1.132 +void 1.133 +OpenFileFinder::Close() 1.134 +{ 1.135 + if (mFdDir) { 1.136 + closedir(mFdDir); 1.137 + } 1.138 + if (mProcDir) { 1.139 + closedir(mProcDir); 1.140 + } 1.141 +} 1.142 + 1.143 +void 1.144 +OpenFileFinder::FillInfo(OpenFileFinder::Info* aInfo, const nsACString& aPath) 1.145 +{ 1.146 + aInfo->mFileName = aPath; 1.147 + aInfo->mPid = mPid; 1.148 + nsPrintfCString exePath("/proc/%d/exe", mPid); 1.149 + ReadSymLink(exePath, aInfo->mExe); 1.150 + aInfo->mComm.Truncate(); 1.151 + aInfo->mAppName.Truncate(); 1.152 + nsPrintfCString statPath("/proc/%d/stat", mPid); 1.153 + nsCString statString; 1.154 + statString.SetLength(200); 1.155 + char *stat = statString.BeginWriting(); 1.156 + if (!stat) { 1.157 + return; 1.158 + } 1.159 + ReadSysFile(statPath.get(), stat, statString.Length()); 1.160 + // The stat line includes the comm field, surrounded by parenthesis. 1.161 + // However, the contents of the comm field itself is arbitrary and 1.162 + // and can include ')', so we search for the rightmost ) as being 1.163 + // the end of the comm field. 1.164 + char *closeParen = strrchr(stat, ')'); 1.165 + if (!closeParen) { 1.166 + return; 1.167 + } 1.168 + char *openParen = strchr(stat, '('); 1.169 + if (!openParen) { 1.170 + return; 1.171 + } 1.172 + if (openParen >= closeParen) { 1.173 + return; 1.174 + } 1.175 + nsDependentCSubstring comm(&openParen[1], closeParen - openParen - 1); 1.176 + aInfo->mComm = comm; 1.177 + // There is a single character field after the comm and then 1.178 + // the parent pid (the field we're interested in). 1.179 + // ) X ppid 1.180 + // 01234 1.181 + int ppid = atoi(&closeParen[4]); 1.182 + 1.183 + if (mPid == mMyPid) { 1.184 + // This is chrome process 1.185 + aInfo->mIsB2gOrDescendant = true; 1.186 + DBG("Chrome process has open file(s)"); 1.187 + return; 1.188 + } 1.189 + // For the rest (non-chrome process), we recursively check the ppid to know 1.190 + // it is a descendant of b2g or not. See bug 931456. 1.191 + while (ppid != mMyPid && ppid != 1) { 1.192 + DBG("Process(%d) is not forked from b2g(%d) or Init(1), keep looking", 1.193 + ppid, mMyPid); 1.194 + nsPrintfCString ppStatPath("/proc/%d/stat", ppid); 1.195 + ReadSysFile(ppStatPath.get(), stat, statString.Length()); 1.196 + closeParen = strrchr(stat, ')'); 1.197 + if (!closeParen) { 1.198 + return; 1.199 + } 1.200 + ppid = atoi(&closeParen[4]); 1.201 + } 1.202 + if (ppid == 1) { 1.203 + // This is a not a b2g process. 1.204 + DBG("Non-b2g process has open file(s)"); 1.205 + aInfo->mIsB2gOrDescendant = false; 1.206 + return; 1.207 + } 1.208 + if (ppid == mMyPid) { 1.209 + // This is a descendant of b2g. 1.210 + DBG("Child process of chrome process has open file(s)"); 1.211 + aInfo->mIsB2gOrDescendant = true; 1.212 + } 1.213 + 1.214 + // This looks like a content process. The comm field will be the 1.215 + // app name. 1.216 + aInfo->mAppName = aInfo->mComm; 1.217 +} 1.218 + 1.219 +bool 1.220 +OpenFileFinder::ReadSymLink(const nsACString& aSymLink, nsACString& aOutPath) 1.221 +{ 1.222 + aOutPath.Truncate(); 1.223 + const char *symLink = aSymLink.BeginReading(); 1.224 + 1.225 + // Verify that we actually have a symlink. 1.226 + struct stat st; 1.227 + if (lstat(symLink, &st)) { 1.228 + return false; 1.229 + } 1.230 + if ((st.st_mode & S_IFMT) != S_IFLNK) { 1.231 + return false; 1.232 + } 1.233 + 1.234 + // Contrary to the documentation st.st_size doesn't seem to be a reliable 1.235 + // indication of the length when reading from /proc, so we use a fixed 1.236 + // size buffer instead. 1.237 + 1.238 + char resolvedSymLink[PATH_MAX]; 1.239 + ssize_t pathLength = readlink(symLink, resolvedSymLink, 1.240 + sizeof(resolvedSymLink) - 1); 1.241 + if (pathLength <= 0) { 1.242 + return false; 1.243 + } 1.244 + resolvedSymLink[pathLength] = '\0'; 1.245 + aOutPath.Assign(resolvedSymLink); 1.246 + return true; 1.247 +} 1.248 + 1.249 +} // system 1.250 +} // mozilla