blob: c3c9904dbd31afff63ebdedf3af38b5b95bab461 [file] [log] [blame]
San Mehat586536c2010-02-16 17:12:00 -08001/*
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 continue;
102
103 // append the file name, after truncating to parent directory
104 path[parent_length] = 0;
105 strcat(path, de->d_name);
106
107 char link[PATH_MAX];
108
109 if (readSymLink(path, link, sizeof(link)) && pathMatchesMountPoint(link, mountPoint)) {
110 if (openFilename) {
111 memset(openFilename, 0, max);
112 strncpy(openFilename, link, max-1);
113 }
114 closedir(dir);
115 return 1;
116 }
117 }
118
119 closedir(dir);
120 return 0;
121}
122
123int Process::checkFileMaps(int pid, const char *mountPoint) {
124 return checkFileMaps(pid, mountPoint, NULL, 0);
125}
126
127int Process::checkFileMaps(int pid, const char *mountPoint, char *openFilename, size_t max) {
128 FILE *file;
129 char buffer[PATH_MAX + 100];
130
131 sprintf(buffer, "/proc/%d/maps", pid);
132 file = fopen(buffer, "r");
133 if (!file)
134 return 0;
135
136 while (fgets(buffer, sizeof(buffer), file)) {
137 // skip to the path
138 const char* path = strchr(buffer, '/');
139 if (path && pathMatchesMountPoint(path, mountPoint)) {
140 if (openFilename) {
141 memset(openFilename, 0, max);
142 strncpy(openFilename, path, max-1);
143 }
144 fclose(file);
145 return 1;
146 }
147 }
148
149 fclose(file);
150 return 0;
151}
152
153int Process::checkSymLink(int pid, const char *mountPoint, const char *name) {
154 char path[PATH_MAX];
155 char link[PATH_MAX];
156
157 sprintf(path, "/proc/%d/%s", pid, name);
158 if (readSymLink(path, link, sizeof(link)) && pathMatchesMountPoint(link, mountPoint))
159 return 1;
160 return 0;
161}
162
163int Process::getPid(const char *s) {
164 int result = 0;
165 while (*s) {
166 if (!isdigit(*s)) return -1;
167 result = 10 * result + (*s++ - '0');
168 }
169 return result;
170}
171
172/*
173 * Hunt down processes that have files open at the given mount point.
174 * action = 0 to just warn,
175 * action = 1 to SIGHUP,
176 * action = 2 to SIGKILL
177 */
178// hunt down and kill processes that have files open on the given mount point
179void Process::killProcessesWithOpenFiles(const char *path, int action) {
180 DIR* dir;
181 struct dirent* de;
182
183 if (!(dir = opendir("/proc"))) {
184 LOGE("opendir failed (%s)", strerror(errno));
185 return;
186 }
187
188 while ((de = readdir(dir))) {
189 int killed = 0;
190 int pid = getPid(de->d_name);
191 char name[PATH_MAX];
192
193 if (pid == -1)
194 continue;
195 getProcessName(pid, name, sizeof(name));
196
197 char openfile[PATH_MAX];
198
199 if (checkFileDescriptorSymLinks(pid, path, openfile, sizeof(openfile))) {
200 LOGE("Process %s (%d) has open file %s", name, pid, openfile);
201 } else if (checkFileMaps(pid, path, openfile, sizeof(openfile))) {
202 LOGE("Process %s (%d) has open filemap for %s", name, pid, openfile);
203 } else if (checkSymLink(pid, path, "cwd")) {
204 LOGE("Process %s (%d) has cwd within %s", name, pid, path);
205 } else if (checkSymLink(pid, path, "root")) {
206 LOGE("Process %s (%d) has chroot within %s", name, pid, path);
207 } else if (checkSymLink(pid, path, "exe")) {
208 LOGE("Process %s (%d) has executable path within %s", name, pid, path);
209 } else {
210 continue;
211 }
212 if (action == 1) {
213 LOGW("Sending SIGHUP to process %d", pid);
214 kill(pid, SIGTERM);
215 } else if (action == 2) {
216 LOGE("Sending SIGKILL to process %d", pid);
217 kill(pid, SIGKILL);
218 }
219 }
220 closedir(dir);
221}