vold: Refactor Processkiller and add command to return users of a mount point

Signed-off-by: San Mehat <san@google.com>
diff --git a/Android.mk b/Android.mk
index d90b399..9036f78 100644
--- a/Android.mk
+++ b/Android.mk
@@ -20,7 +20,7 @@
                   Volume.cpp                           \
                   DirectVolume.cpp                     \
                   logwrapper.c                         \
-                  ProcessKiller.c                      \
+                  Process.cpp                          \
                   geom_mbr_enc.c                       \
                   Fat.cpp                              \
                   Loop.cpp                             \
diff --git a/CommandListener.cpp b/CommandListener.cpp
index 59c431f..b25728f 100644
--- a/CommandListener.cpp
+++ b/CommandListener.cpp
@@ -30,12 +30,14 @@
 #include "CommandListener.h"
 #include "VolumeManager.h"
 #include "ResponseCode.h"
+#include "Process.h"
 
 CommandListener::CommandListener() :
                  FrameworkListener("vold") {
     registerCmd(new VolumeCmd());
     registerCmd(new AsecCmd());
     registerCmd(new ShareCmd());
+    registerCmd(new StorageCmd());
 }
 
 CommandListener::VolumeCmd::VolumeCmd() :
@@ -134,6 +136,55 @@
     return 0;
 }
 
+CommandListener::StorageCmd::StorageCmd() :
+                 VoldCommand("storage") {
+}
+
+int CommandListener::StorageCmd::runCommand(SocketClient *cli,
+                                                      int argc, char **argv) {
+    if (argc < 2) {
+        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
+        return 0;
+    }
+
+    if (!strcmp(argv[1], "users")) {
+        DIR *dir;
+        struct dirent *de;
+
+        if (!(dir = opendir("/proc"))) {
+            cli->sendMsg(ResponseCode::OperationFailed, "Failed to open /proc", true);
+            return 0;
+        }
+
+        while ((de = readdir(dir))) {
+            int pid = Process::getPid(de->d_name);
+
+            if (pid < 0) {
+                continue;
+            }
+
+            char processName[255];
+            Process::getProcessName(pid, processName, sizeof(processName));
+
+            if (Process::checkFileDescriptorSymLinks(pid, argv[2]) ||
+                Process::checkFileMaps(pid, argv[2]) ||
+                Process::checkSymLink(pid, argv[2], "cwd") ||
+                Process::checkSymLink(pid, argv[2], "root") ||
+                Process::checkSymLink(pid, argv[2], "exe")) {
+
+                char msg[1024];
+                snprintf(msg, sizeof(msg), "%d %s", pid, processName);
+                cli->sendMsg(ResponseCode::StorageUsersListResult, msg, false);
+            }
+        }
+        closedir(dir);
+        cli->sendMsg(ResponseCode::CommandOkay, "Storage user list complete", false);
+    } else {
+        cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown storage cmd", false);
+    }
+    return 0;
+}
+
 CommandListener::AsecCmd::AsecCmd() :
                  VoldCommand("asec") {
 }
diff --git a/CommandListener.h b/CommandListener.h
index e6fa805..7d4560f 100644
--- a/CommandListener.h
+++ b/CommandListener.h
@@ -48,6 +48,12 @@
         int runCommand(SocketClient *c, int argc, char ** argv);
     };
 
+    class StorageCmd : public VoldCommand {
+    public:
+        StorageCmd();
+        virtual ~StorageCmd() {}
+        int runCommand(SocketClient *c, int argc, char ** argv);
+    };
 };
 
 #endif
diff --git a/Process.cpp b/Process.cpp
new file mode 100644
index 0000000..c3c9904
--- /dev/null
+++ b/Process.cpp
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <poll.h>
+#include <sys/stat.h>
+#include <signal.h>
+
+#define LOG_TAG "ProcessKiller"
+#include <cutils/log.h>
+
+#include "Process.h"
+
+int Process::readSymLink(const char *path, char *link, size_t max) {
+    struct stat s;
+    int length;
+
+    if (lstat(path, &s) < 0)
+        return 0;
+    if ((s.st_mode & S_IFMT) != S_IFLNK)
+        return 0;
+   
+    // we have a symlink    
+    length = readlink(path, link, max- 1);
+    if (length <= 0) 
+        return 0;
+    link[length] = 0;
+    return 1;
+}
+
+int Process::pathMatchesMountPoint(const char* path, const char* mountPoint) {
+    int length = strlen(mountPoint);
+    if (length > 1 && strncmp(path, mountPoint, length) == 0) {
+        // we need to do extra checking if mountPoint does not end in a '/'
+        if (mountPoint[length - 1] == '/')
+            return 1;
+        // if mountPoint does not have a trailing slash, we need to make sure
+        // there is one in the path to avoid partial matches.
+        return (path[length] == 0 || path[length] == '/');
+    }
+    
+    return 0;
+}
+
+void Process::getProcessName(int pid, char *buffer, size_t max) {
+    int fd;
+    snprintf(buffer, max, "/proc/%d/cmdline", pid);
+    fd = open(buffer, O_RDONLY);
+    if (fd < 0) {
+        strcpy(buffer, "???");
+    } else {
+        int length = read(fd, buffer, max - 1);
+        buffer[length] = 0;
+        close(fd);
+    }
+}
+
+int Process::checkFileDescriptorSymLinks(int pid, const char *mountPoint) {
+    return checkFileDescriptorSymLinks(pid, mountPoint, NULL, 0);
+}
+
+int Process::checkFileDescriptorSymLinks(int pid, const char *mountPoint, char *openFilename, size_t max) {
+
+
+    // compute path to process's directory of open files
+    char    path[PATH_MAX];
+    sprintf(path, "/proc/%d/fd", pid);
+    DIR *dir = opendir(path);
+    if (!dir)
+        return 0;
+
+    // remember length of the path
+    int parent_length = strlen(path);
+    // append a trailing '/'
+    path[parent_length++] = '/';
+
+    struct dirent* de;
+    while ((de = readdir(dir))) {
+        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+            continue;
+        
+        // append the file name, after truncating to parent directory
+        path[parent_length] = 0;
+        strcat(path, de->d_name);
+
+        char link[PATH_MAX];
+
+        if (readSymLink(path, link, sizeof(link)) && pathMatchesMountPoint(link, mountPoint)) {
+            if (openFilename) {
+                memset(openFilename, 0, max);
+                strncpy(openFilename, link, max-1);
+            }
+            closedir(dir);
+            return 1;
+        }
+    }
+
+    closedir(dir);
+    return 0;
+}
+
+int Process::checkFileMaps(int pid, const char *mountPoint) {
+    return checkFileMaps(pid, mountPoint, NULL, 0);
+}
+
+int Process::checkFileMaps(int pid, const char *mountPoint, char *openFilename, size_t max) {
+    FILE *file;
+    char buffer[PATH_MAX + 100];
+
+    sprintf(buffer, "/proc/%d/maps", pid);
+    file = fopen(buffer, "r");
+    if (!file)
+        return 0;
+    
+    while (fgets(buffer, sizeof(buffer), file)) {
+        // skip to the path
+        const char* path = strchr(buffer, '/');
+        if (path && pathMatchesMountPoint(path, mountPoint)) {
+            if (openFilename) {
+                memset(openFilename, 0, max);
+                strncpy(openFilename, path, max-1);
+            }
+            fclose(file);
+            return 1;
+        }
+    }
+    
+    fclose(file);
+    return 0;
+}
+
+int Process::checkSymLink(int pid, const char *mountPoint, const char *name) {
+    char    path[PATH_MAX];
+    char    link[PATH_MAX];
+
+    sprintf(path, "/proc/%d/%s", pid, name);
+    if (readSymLink(path, link, sizeof(link)) && pathMatchesMountPoint(link, mountPoint)) 
+        return 1;
+    return 0;
+}
+
+int Process::getPid(const char *s) {
+    int result = 0;
+    while (*s) {
+        if (!isdigit(*s)) return -1;
+        result = 10 * result + (*s++ - '0');
+    }
+    return result;
+}
+
+/*
+ * Hunt down processes that have files open at the given mount point.
+ * action = 0 to just warn,
+ * action = 1 to SIGHUP,
+ * action = 2 to SIGKILL
+ */
+// hunt down and kill processes that have files open on the given mount point
+void Process::killProcessesWithOpenFiles(const char *path, int action) {
+    DIR*    dir;
+    struct dirent* de;
+
+    if (!(dir = opendir("/proc"))) {
+        LOGE("opendir failed (%s)", strerror(errno));
+        return;
+    }
+
+    while ((de = readdir(dir))) {
+        int killed = 0;
+        int pid = getPid(de->d_name);
+        char name[PATH_MAX];
+
+        if (pid == -1)
+            continue;
+        getProcessName(pid, name, sizeof(name));
+
+        char openfile[PATH_MAX];
+
+        if (checkFileDescriptorSymLinks(pid, path, openfile, sizeof(openfile))) {
+            LOGE("Process %s (%d) has open file %s", name, pid, openfile);
+        } else if (checkFileMaps(pid, path, openfile, sizeof(openfile))) {
+            LOGE("Process %s (%d) has open filemap for %s", name, pid, openfile);
+        } else if (checkSymLink(pid, path, "cwd")) {
+            LOGE("Process %s (%d) has cwd within %s", name, pid, path);
+        } else if (checkSymLink(pid, path, "root")) {
+            LOGE("Process %s (%d) has chroot within %s", name, pid, path);
+        } else if (checkSymLink(pid, path, "exe")) {
+            LOGE("Process %s (%d) has executable path within %s", name, pid, path);
+        } else {
+            continue;
+        }
+        if (action == 1) {
+            LOGW("Sending SIGHUP to process %d", pid);
+            kill(pid, SIGTERM);
+        } else if (action == 2) {
+            LOGE("Sending SIGKILL to process %d", pid);
+            kill(pid, SIGKILL);
+        }
+    }
+    closedir(dir);
+}
diff --git a/Process.h b/Process.h
new file mode 100644
index 0000000..fc0c0b7
--- /dev/null
+++ b/Process.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _PROCESS_H
+#define _PROCESS_H
+
+class Process {
+public:
+    static void killProcessesWithOpenFiles(const char *path, int action);
+    static int getPid(const char *s);
+    static int checkSymLink(int pid, const char *path, const char *name);
+    static int checkFileMaps(int pid, const char *path);
+    static int checkFileMaps(int pid, const char *path, char *openFilename, size_t max);
+    static int checkFileDescriptorSymLinks(int pid, const char *mountPoint);
+    static int checkFileDescriptorSymLinks(int pid, const char *mountPoint, char *openFilename, size_t max);
+    static void getProcessName(int pid, char *buffer, size_t max);
+private:
+    static int readSymLink(const char *path, char *link, size_t max);
+    static int pathMatchesMountPoint(const char *path, const char *mountPoint);
+};
+
+#endif
diff --git a/ProcessKiller.c b/ProcessKiller.c
deleted file mode 100644
index 7375ef3..0000000
--- a/ProcessKiller.c
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
-** mountd process killer
-*/
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <ctype.h>
-#include <pwd.h>
-#include <stdlib.h>
-#include <poll.h>
-#include <sys/stat.h>
-#include <signal.h>
-
-#define LOG_TAG "ProcessKiller"
-#include <cutils/log.h>
-
-#define PATH_MAX 4096
-
-static int ReadSymLink(const char* path, char* link)
-{
-    struct stat s;
-    int length;
-
-    if (lstat(path, &s) < 0)
-        return 0;
-    if ((s.st_mode & S_IFMT) != S_IFLNK)
-        return 0;
-   
-    // we have a symlink    
-    length = readlink(path, link, PATH_MAX - 1);
-    if (length <= 0) 
-        return 0;
-    link[length] = 0;
-    return 1;
-}
-
-static int PathMatchesMountPoint(const char* path, const char* mountPoint)
-{
-    int length = strlen(mountPoint);
-    if (length > 1 && strncmp(path, mountPoint, length) == 0)
-    {
-        // we need to do extra checking if mountPoint does not end in a '/'
-        if (mountPoint[length - 1] == '/')
-            return 1;
-        // if mountPoint does not have a trailing slash, we need to make sure
-        // there is one in the path to avoid partial matches.
-        return (path[length] == 0 || path[length] == '/');
-    }
-    
-    return 0;
-}
-
-static void GetProcessName(int pid, char buffer[PATH_MAX])
-{
-    int fd;
-    sprintf(buffer, "/proc/%d/cmdline", pid);
-    fd = open(buffer, O_RDONLY);
-    if (fd < 0) {
-        strcpy(buffer, "???");
-    } else {
-        int length = read(fd, buffer, PATH_MAX - 1);
-        buffer[length] = 0;
-        close(fd);
-    }
-}
-
-static int CheckFileDescriptorSymLinks(int pid, const char* mountPoint)
-{
-    DIR*    dir;
-    struct dirent* de;
-    int fileOpen = 0;
-    char    path[PATH_MAX];
-    char    link[PATH_MAX];
-    int     parent_length;
-
-    // compute path to process's directory of open files
-    sprintf(path, "/proc/%d/fd", pid);
-    dir = opendir(path);
-    if (!dir)
-        return 0;
-
-    // remember length of the path
-    parent_length = strlen(path);
-    // append a trailing '/'
-    path[parent_length++] = '/';
-    
-    while ((de = readdir(dir)) != 0 && !fileOpen) {
-        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
-            continue;
-        
-        // append the file name, after truncating to parent directory
-        path[parent_length] = 0;
-        strcat(path, de->d_name);
-
-        if (ReadSymLink(path, link) && PathMatchesMountPoint(link, mountPoint))
-        {
-            char    name[PATH_MAX];
-            GetProcessName(pid, name);
-            LOGE("Process %s (%d) has open file %s", name, pid, link);
-            fileOpen = 1;
-        }
-    }
-
-    closedir(dir);
-    return fileOpen;
-}
-
-static int CheckFileMaps(int pid, const char* mountPoint)
-{
-    FILE*   file;
-    char    buffer[PATH_MAX + 100];
-    int mapOpen = 0;
-
-    sprintf(buffer, "/proc/%d/maps", pid);
-    file = fopen(buffer, "r");
-    if (!file)
-        return 0;
-    
-    while (!mapOpen && fgets(buffer, sizeof(buffer), file))
-    {
-        // skip to the path
-        const char* path = strchr(buffer, '/');
-        if (path && PathMatchesMountPoint(path, mountPoint))
-        {
-            char    name[PATH_MAX];
-            GetProcessName(pid, name);
-            LOGE("process %s (%d) has open file map for %s", name, pid, path);
-            mapOpen = 1;
-        }
-    }
-    
-    fclose(file);
-    return mapOpen;
-}
-
-static int CheckSymLink(int pid, const char* mountPoint, const char* name, const char* message)
-{
-    char    path[PATH_MAX];
-    char    link[PATH_MAX];
-
-    sprintf(path, "/proc/%d/%s", pid, name);
-    if (ReadSymLink(path, link) && PathMatchesMountPoint(link, mountPoint)) 
-    {
-        char    name[PATH_MAX];
-        GetProcessName(pid, name);
-        LOGW("Process %s (%d) has %s in %s", name, pid, message, mountPoint);
-        return 1;
-    }
-    else
-        return 0;
-}
-
-static int get_pid(const char* s)
-{
-    int result = 0;
-    while (*s) {
-        if (!isdigit(*s)) return -1;
-        result = 10 * result + (*s++ - '0');
-    }
-    return result;
-}
-
-/*
- * Hunt down processes that have files open at the given mount point.
- * action = 0 to just warn,
- * action = 1 to SIGHUP,
- * action = 2 to SIGKILL
- */
-// hunt down and kill processes that have files open on the given mount point
-void KillProcessesWithOpenFiles(const char* mountPoint, int action)
-{
-    DIR*    dir;
-    struct dirent* de;
-
-    dir = opendir("/proc");
-    if (!dir) return;
-
-    while ((de = readdir(dir)) != 0)
-    {
-        int killed = 0;
-        // does the name look like a process ID?
-        int pid = get_pid(de->d_name);
-        if (pid == -1) continue;
-
-        if (CheckFileDescriptorSymLinks(pid, mountPoint)    // check for open files
-                || CheckFileMaps(pid, mountPoint)           // check for mmap()
-                || CheckSymLink(pid, mountPoint, "cwd", "working directory")    // check working directory
-                || CheckSymLink(pid, mountPoint, "root", "chroot")              // check for chroot()
-                || CheckSymLink(pid, mountPoint, "exe", "executable path")      // check executable path
-            ) 
-        {
-            if (action == 1) {
-                LOGW("Sending SIGHUP to process %d", pid);
-                kill(pid, SIGTERM);
-            } else if (action == 2) {
-                LOGE("Sending SIGKILL to process %d", pid);
-                kill(pid, SIGKILL);
-            }
-        }
-    }
-
-    closedir(dir);
-}        
diff --git a/ResponseCode.h b/ResponseCode.h
index 9d775f9..3508f81 100644
--- a/ResponseCode.h
+++ b/ResponseCode.h
@@ -23,8 +23,9 @@
     // before proceeding with a new command.
     static const int ActionInitiated  = 100;
 
-    static const int VolumeListResult = 110;
-    static const int AsecListResult   = 111;
+    static const int VolumeListResult         = 110;
+    static const int AsecListResult           = 111;
+    static const int StorageUsersListResult   = 112;
 
     // 200 series - Requested action has been successfully completed
     static const int CommandOkay              = 200;
diff --git a/Volume.cpp b/Volume.cpp
index af69bc2..529975a 100644
--- a/Volume.cpp
+++ b/Volume.cpp
@@ -40,8 +40,8 @@
 #include "VolumeManager.h"
 #include "ResponseCode.h"
 #include "Fat.h"
+#include "Process.h"
 
-extern "C" void KillProcessesWithOpenFiles(const char *, int);
 extern "C" void dos_partition_dec(void const *pp, struct dos_partition *d);
 extern "C" void dos_partition_enc(void *pp, struct dos_partition *d);
 
@@ -308,7 +308,7 @@
         } else
             action = 0; // just complain
 
-        KillProcessesWithOpenFiles(getMountpoint(), action);
+        Process::killProcessesWithOpenFiles(getMountpoint(), action);
         usleep(1000*250);
     }
 
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index d778ce2..b21a36e 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -37,8 +37,7 @@
 #include "Loop.h"
 #include "Fat.h"
 #include "Devmapper.h"
-
-extern "C" void KillProcessesWithOpenFiles(const char *, int);
+#include "Process.h"
 
 VolumeManager *VolumeManager::sInstance = NULL;
 
@@ -356,7 +355,7 @@
         else
             action = 0; // Just complain
 
-        KillProcessesWithOpenFiles(mountPoint, action);
+        Process::killProcessesWithOpenFiles(mountPoint, action);
         usleep(1000 * 1000);
     }