author | Dave Hylands <dhylands@mozilla.com> |
Tue, 16 Jul 2013 13:14:09 -0700 | |
changeset 138760 | d556762ff01220e66990f16c91005d0dab2019a5 |
parent 138719 | fd10ead17acea3be28ab8b485c499b0ccd142fbb |
child 138761 | 1350632b8fe91faa0b5d24ceeca993d754c3c3b1 |
push id | 24968 |
push user | emorley@mozilla.com |
push date | Wed, 17 Jul 2013 14:42:00 +0000 |
treeherder | autoland@5a86438c4093 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | qdot |
bugs | 876782 |
milestone | 25.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/system/gonk/AutoMounter.cpp +++ b/dom/system/gonk/AutoMounter.cpp @@ -23,16 +23,17 @@ #include "mozilla/FileUtils.h" #include "mozilla/Hal.h" #include "mozilla/StaticPtr.h" #include "nsAutoPtr.h" #include "nsMemory.h" #include "nsString.h" #include "nsThreadUtils.h" #include "nsXULAppAPI.h" +#include "OpenFileFinder.h" #include "Volume.h" #include "VolumeManager.h" using namespace mozilla::hal; /************************************************************************** * * The following "switch" files are available for monitoring usb @@ -66,18 +67,19 @@ using namespace mozilla::hal; #define ICS_SYS_USB_FUNCTIONS "/sys/devices/virtual/android_usb/android0/functions" #define ICS_SYS_UMS_DIRECTORY "/sys/devices/virtual/android_usb/android0/f_mass_storage" #define ICS_SYS_USB_STATE "/sys/devices/virtual/android_usb/android0/state" #define USE_DEBUG 0 #undef LOG -#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "AutoMounter" , ## args) -#define ERR(args...) __android_log_print(ANDROID_LOG_ERROR, "AutoMounter" , ## args) +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "AutoMounter", ## args) +#define LOGW(args...) __android_log_print(ANDROID_LOG_WARN, "AutoMounter", ## args) +#define ERR(args...) __android_log_print(ANDROID_LOG_ERROR, "AutoMounter", ## args) #if USE_DEBUG #define DBG(args...) __android_log_print(ANDROID_LOG_DEBUG, "AutoMounter" , ## args) #else #define DBG(args...) #endif namespace mozilla { @@ -397,50 +399,81 @@ AutoMounter::UpdateState() if (tryToShare && vol->IsSharingEnabled()) { // We're going to try to unmount and share the volumes switch (volState) { case nsIVolume::STATE_MOUNTED: { if (vol->IsMountLocked()) { // The volume is currently locked, so leave it in the mounted // state. - DBG("UpdateState: Mounted volume %s is locked, leaving", - vol->NameStr()); + LOGW("UpdateState: Mounted volume %s is locked, not sharing", + vol->NameStr()); break; } + + // Check to see if there are any open files on the volume and + // don't initiate the unmount while there are open files. + OpenFileFinder::Info fileInfo; + OpenFileFinder fileFinder(vol->MountPoint()); + if (fileFinder.First(&fileInfo)) { + LOGW("The following files are open under '%s'", + vol->MountPoint().get()); + do { + LOGW(" PID: %d file: '%s' app: '%s' comm: '%s' exe: '%s'\n", + fileInfo.mPid, + fileInfo.mFileName.get(), + fileInfo.mAppName.get(), + fileInfo.mComm.get(), + fileInfo.mExe.get()); + } while (fileFinder.Next(&fileInfo)); + LOGW("UpdateState: Mounted volume %s has open files, not sharing", + vol->NameStr()); + + // Check again in 5 seconds to see if the files are closed. Since + // we're trying to share the volume, this implies that we're + // plugged into the PC via USB and this in turn implies that the + // battery is charging, so we don't need to be too concerned about + // wasting battery here. + MessageLoopForIO::current()-> + PostDelayedTask(FROM_HERE, + NewRunnableMethod(this, &AutoMounter::UpdateState), + 5000); + break; + } + // Volume is mounted, we need to unmount before // we can share. - DBG("UpdateState: Unmounting %s", vol->NameStr()); + LOG("UpdateState: Unmounting %s", vol->NameStr()); vol->StartUnmount(mResponseCallback); return; // UpdateState will be called again when the Unmount command completes } case nsIVolume::STATE_IDLE: { // Volume is unmounted. We can go ahead and share. - DBG("UpdateState: Sharing %s", vol->NameStr()); + LOG("UpdateState: Sharing %s", vol->NameStr()); vol->StartShare(mResponseCallback); return; // UpdateState will be called again when the Share command completes } default: { // Not in a state that we can do anything about. break; } } } else { // We're going to try and unshare and remount the volumes switch (volState) { case nsIVolume::STATE_SHARED: { // Volume is shared. We can go ahead and unshare. - DBG("UpdateState: Unsharing %s", vol->NameStr()); + LOG("UpdateState: Unsharing %s", vol->NameStr()); vol->StartUnshare(mResponseCallback); return; // UpdateState will be called again when the Unshare command completes } case nsIVolume::STATE_IDLE: { // Volume is unmounted, try to mount. - DBG("UpdateState: Mounting %s", vol->NameStr()); + LOG("UpdateState: Mounting %s", vol->NameStr()); vol->StartMount(mResponseCallback); return; // UpdateState will be called again when Mount command completes } default: { // Not in a state that we can do anything about. break; } }
new file mode 100644 --- /dev/null +++ b/dom/system/gonk/OpenFileFinder.cpp @@ -0,0 +1,195 @@ +/* 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> + +namespace mozilla { +namespace system { + +OpenFileFinder::OpenFileFinder(const nsACString& aPath) + : mPath(aPath), + mProcDir(nullptr), + mFdDir(nullptr), + mPid(0) +{ +} + +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); + return true; + } + } + // 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]); + // We assume that we're running in the parent process + if (ppid != getpid()) { + return; + } + // 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
new file mode 100644 --- /dev/null +++ b/dom/system/gonk/OpenFileFinder.h @@ -0,0 +1,60 @@ +/* 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/. */ + +#ifndef mozilla_system_openfilefinder_h__ +#define mozilla_system_openfilefinder_h__ + +#include "nsString.h" + +#include <dirent.h> + +namespace mozilla { +namespace system { + +class OpenFileFinder +{ +public: + enum State + { + NEXT_PID, + CHECK_FDS, + DONE + }; + class Info + { + public: + nsCString mFileName; // name of the the open file + nsCString mAppName; // App which has the file open (if it's a b2g app) + pid_t mPid; // pid of the process which has the file open + nsCString mComm; // comm associated with pid + nsCString mExe; // executable name associated with pid + }; + + OpenFileFinder(const nsACString& aPath); + ~OpenFileFinder(); + + bool First(Info* aInfo); // Return the first open file + bool Next(Info* aInfo); // Return the next open file + void Close(); + +private: + + void FillInfo(Info *aInfo, const nsACString& aPath); + bool ReadSymLink(const nsACString& aSymLink, nsACString& aOutPath); + bool PathMatches(const nsACString& aPath) + { + return Substring(aPath, 0, mPath.Length()).Equals(mPath); + } + + State mState; // Keeps track of what we're doing. + nsCString mPath; // Only report files contained within this directory tree + DIR* mProcDir; // Used for scanning /proc + DIR* mFdDir; // Used for scanning /proc/PID/fd + int mPid; // PID currently being processed +}; + +} // system +} // mozilla + +#endif // mozilla_system_nsvolume_h__
--- a/dom/system/gonk/moz.build +++ b/dom/system/gonk/moz.build @@ -47,16 +47,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk 'AutoMounter.cpp', 'AutoMounterSetting.cpp', 'GonkGPSGeolocationProvider.cpp', 'AudioChannelManager.cpp', 'nsVolume.cpp', 'nsVolumeMountLock.cpp', 'nsVolumeService.cpp', 'nsVolumeStat.cpp', + 'OpenFileFinder.cpp', 'TimeZoneSettingObserver.cpp', 'Volume.cpp', 'VolumeCommand.cpp', 'VolumeManager.cpp', 'VolumeServiceIOThread.cpp', 'VolumeServiceTest.cpp', ]