1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <stdio.h>
18#include <unistd.h>
19#include <errno.h>
20#include <string.h>
21#include <fcntl.h>
22#include <dirent.h>
23#include <ctype.h>
24#include <pwd.h>
25#include <stdlib.h>
26#include <poll.h>
27#include <sys/stat.h>
28#include <signal.h>
29
30#define LOG_TAG "ProcessKiller"
31#include <cutils/log.h>
32
33#include "Process.h"
34
35int Process::readSymLink(const char *path, char *link, size_t max) {
36    struct stat s;
37    int length;
38
39    if (lstat(path, &s) < 0)
40        return 0;
41    if ((s.st_mode & S_IFMT) != S_IFLNK)
42        return 0;
43
44    // we have a symlink
45    length = readlink(path, link, max- 1);
46    if (length <= 0)
47        return 0;
48    link[length] = 0;
49    return 1;
50}
51
52int Process::pathMatchesMountPoint(const char* path, const char* mountPoint) {
53    int length = strlen(mountPoint);
54    if (length > 1 && strncmp(path, mountPoint, length) == 0) {
55        // we need to do extra checking if mountPoint does not end in a '/'
56        if (mountPoint[length - 1] == '/')
57            return 1;
58        // if mountPoint does not have a trailing slash, we need to make sure
59        // there is one in the path to avoid partial matches.
60        return (path[length] == 0 || path[length] == '/');
61    }
62
63    return 0;
64}
65
66void Process::getProcessName(int pid, char *buffer, size_t max) {
67    int fd;
68    snprintf(buffer, max, "/proc/%d/cmdline", pid);
69    fd = open(buffer, O_RDONLY);
70    if (fd < 0) {
71        strcpy(buffer, "???");
72    } else {
73        int length = read(fd, buffer, max - 1);
74        buffer[length] = 0;
75        close(fd);
76    }
77}
78
79int Process::checkFileDescriptorSymLinks(int pid, const char *mountPoint) {
80    return checkFileDescriptorSymLinks(pid, mountPoint, NULL, 0);
81}
82
83int Process::checkFileDescriptorSymLinks(int pid, const char *mountPoint, char *openFilename, size_t max) {
84
85
86    // compute path to process's directory of open files
87    char    path[PATH_MAX];
88    sprintf(path, "/proc/%d/fd", pid);
89    DIR *dir = opendir(path);
90    if (!dir)
91        return 0;
92
93    // remember length of the path
94    int parent_length = strlen(path);
95    // append a trailing '/'
96    path[parent_length++] = '/';
97
98    struct dirent* de;
99    while ((de = readdir(dir))) {
100        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")
101                || strlen(de->d_name) + parent_length + 1 >= PATH_MAX)
102            continue;
103
104        // append the file name, after truncating to parent directory
105        path[parent_length] = 0;
106        strcat(path, de->d_name);
107
108        char link[PATH_MAX];
109
110        if (readSymLink(path, link, sizeof(link)) && pathMatchesMountPoint(link, mountPoint)) {
111            if (openFilename) {
112                memset(openFilename, 0, max);
113                strncpy(openFilename, link, max-1);
114            }
115            closedir(dir);
116            return 1;
117        }
118    }
119
120    closedir(dir);
121    return 0;
122}
123
124int Process::checkFileMaps(int pid, const char *mountPoint) {
125    return checkFileMaps(pid, mountPoint, NULL, 0);
126}
127
128int Process::checkFileMaps(int pid, const char *mountPoint, char *openFilename, size_t max) {
129    FILE *file;
130    char buffer[PATH_MAX + 100];
131
132    sprintf(buffer, "/proc/%d/maps", pid);
133    file = fopen(buffer, "r");
134    if (!file)
135        return 0;
136
137    while (fgets(buffer, sizeof(buffer), file)) {
138        // skip to the path
139        const char* path = strchr(buffer, '/');
140        if (path && pathMatchesMountPoint(path, mountPoint)) {
141            if (openFilename) {
142                memset(openFilename, 0, max);
143                strncpy(openFilename, path, max-1);
144            }
145            fclose(file);
146            return 1;
147        }
148    }
149
150    fclose(file);
151    return 0;
152}
153
154int Process::checkSymLink(int pid, const char *mountPoint, const char *name) {
155    char    path[PATH_MAX];
156    char    link[PATH_MAX];
157
158    sprintf(path, "/proc/%d/%s", pid, name);
159    if (readSymLink(path, link, sizeof(link)) && pathMatchesMountPoint(link, mountPoint))
160        return 1;
161    return 0;
162}
163
164int Process::getPid(const char *s) {
165    int result = 0;
166    while (*s) {
167        if (!isdigit(*s)) return -1;
168        result = 10 * result + (*s++ - '0');
169    }
170    return result;
171}
172
173/*
174 * Hunt down processes that have files open at the given mount point.
175 * action = 0 to just warn,
176 * action = 1 to SIGHUP,
177 * action = 2 to SIGKILL
178 */
179// hunt down and kill processes that have files open on the given mount point
180void Process::killProcessesWithOpenFiles(const char *path, int action) {
181    DIR*    dir;
182    struct dirent* de;
183
184    if (!(dir = opendir("/proc"))) {
185        SLOGE("opendir failed (%s)", strerror(errno));
186        return;
187    }
188
189    while ((de = readdir(dir))) {
190        int killed = 0;
191        int pid = getPid(de->d_name);
192        char name[PATH_MAX];
193
194        if (pid == -1)
195            continue;
196        getProcessName(pid, name, sizeof(name));
197
198        char openfile[PATH_MAX];
199
200        if (checkFileDescriptorSymLinks(pid, path, openfile, sizeof(openfile))) {
201            SLOGE("Process %s (%d) has open file %s", name, pid, openfile);
202        } else if (checkFileMaps(pid, path, openfile, sizeof(openfile))) {
203            SLOGE("Process %s (%d) has open filemap for %s", name, pid, openfile);
204        } else if (checkSymLink(pid, path, "cwd")) {
205            SLOGE("Process %s (%d) has cwd within %s", name, pid, path);
206        } else if (checkSymLink(pid, path, "root")) {
207            SLOGE("Process %s (%d) has chroot within %s", name, pid, path);
208        } else if (checkSymLink(pid, path, "exe")) {
209            SLOGE("Process %s (%d) has executable path within %s", name, pid, path);
210        } else {
211            continue;
212        }
213        if (action == 1) {
214            SLOGW("Sending SIGHUP to process %d", pid);
215            kill(pid, SIGTERM);
216        } else if (action == 2) {
217            SLOGE("Sending SIGKILL to process %d", pid);
218            kill(pid, SIGKILL);
219        }
220    }
221    closedir(dir);
222}
223