blob: ad36731cc6af727d171d943fc5052c5d28e3f459 [file] [log] [blame]
San Mehata2677e42009-12-13 10:40:18 -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/*
18** mountd process killer
19*/
20
21#include <stdio.h>
22#include <unistd.h>
23#include <string.h>
24#include <fcntl.h>
25#include <dirent.h>
26#include <ctype.h>
27#include <pwd.h>
28#include <stdlib.h>
29#include <poll.h>
30#include <sys/stat.h>
31#include <signal.h>
32
33#define LOG_TAG "ProcessKiller"
34#include <cutils/log.h>
35
36#define PATH_MAX 4096
37
38static int ReadSymLink(const char* path, char* link)
39{
40 struct stat s;
41 int length;
42
43 if (lstat(path, &s) < 0)
44 return 0;
45 if ((s.st_mode & S_IFMT) != S_IFLNK)
46 return 0;
47
48 // we have a symlink
49 length = readlink(path, link, PATH_MAX - 1);
50 if (length <= 0)
51 return 0;
52 link[length] = 0;
53 return 1;
54}
55
56static int PathMatchesMountPoint(const char* path, const char* mountPoint)
57{
58 int length = strlen(mountPoint);
59 if (length > 1 && strncmp(path, mountPoint, length) == 0)
60 {
61 // we need to do extra checking if mountPoint does not end in a '/'
62 if (mountPoint[length - 1] == '/')
63 return 1;
64 // if mountPoint does not have a trailing slash, we need to make sure
65 // there is one in the path to avoid partial matches.
66 return (path[length] == 0 || path[length] == '/');
67 }
68
69 return 0;
70}
71
72static void GetProcessName(int pid, char buffer[PATH_MAX])
73{
74 int fd;
75 sprintf(buffer, "/proc/%d/cmdline", pid);
76 fd = open(buffer, O_RDONLY);
77 if (fd < 0) {
78 strcpy(buffer, "???");
79 } else {
80 int length = read(fd, buffer, PATH_MAX - 1);
81 buffer[length] = 0;
82 close(fd);
83 }
84}
85
86static int CheckFileDescriptorSymLinks(int pid, const char* mountPoint)
87{
88 DIR* dir;
89 struct dirent* de;
90 int fileOpen = 0;
91 char path[PATH_MAX];
92 char link[PATH_MAX];
93 int parent_length;
94
95 // compute path to process's directory of open files
96 sprintf(path, "/proc/%d/fd", pid);
97 dir = opendir(path);
98 if (!dir)
99 return 0;
100
101 // remember length of the path
102 parent_length = strlen(path);
103 // append a trailing '/'
104 path[parent_length++] = '/';
105
106 while ((de = readdir(dir)) != 0 && !fileOpen) {
107 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
108 continue;
109
110 // append the file name, after truncating to parent directory
111 path[parent_length] = 0;
112 strcat(path, de->d_name);
113
114 if (ReadSymLink(path, link) && PathMatchesMountPoint(link, mountPoint))
115 {
116 char name[PATH_MAX];
117 GetProcessName(pid, name);
118 LOGE("Process %s (%d) has open file %s", name, pid, link);
119 fileOpen = 1;
120 }
121 }
122
123 closedir(dir);
124 return fileOpen;
125}
126
127static int CheckFileMaps(int pid, const char* mountPoint)
128{
129 FILE* file;
130 char buffer[PATH_MAX + 100];
131 int mapOpen = 0;
132
133 sprintf(buffer, "/proc/%d/maps", pid);
134 file = fopen(buffer, "r");
135 if (!file)
136 return 0;
137
138 while (!mapOpen && fgets(buffer, sizeof(buffer), file))
139 {
140 // skip to the path
141 const char* path = strchr(buffer, '/');
142 if (path && PathMatchesMountPoint(path, mountPoint))
143 {
144 char name[PATH_MAX];
145 GetProcessName(pid, name);
146 LOGE("process %s (%d) has open file map for %s", name, pid, path);
147 mapOpen = 1;
148 }
149 }
150
151 fclose(file);
152 return mapOpen;
153}
154
155static int CheckSymLink(int pid, const char* mountPoint, const char* name, const char* message)
156{
157 char path[PATH_MAX];
158 char link[PATH_MAX];
159
160 sprintf(path, "/proc/%d/%s", pid, name);
161 if (ReadSymLink(path, link) && PathMatchesMountPoint(link, mountPoint))
162 {
163 char name[PATH_MAX];
164 GetProcessName(pid, name);
165 LOGW("Process %s (%d) has %s in %s", name, pid, message, mountPoint);
166 return 1;
167 }
168 else
169 return 0;
170}
171
172static int get_pid(const char* s)
173{
174 int result = 0;
175 while (*s) {
176 if (!isdigit(*s)) return -1;
177 result = 10 * result + (*s++ - '0');
178 }
179 return result;
180}
181
182// hunt down and kill processes that have files open on the given mount point
183void KillProcessesWithOpenFiles(const char* mountPoint, int sigkill, int *excluded, int num_excluded)
184{
185 DIR* dir;
186 struct dirent* de;
187
188 dir = opendir("/proc");
189 if (!dir) return;
190
191 while ((de = readdir(dir)) != 0)
192 {
193 int killed = 0;
194 // does the name look like a process ID?
195 int pid = get_pid(de->d_name);
196 if (pid == -1) continue;
197
198 if (CheckFileDescriptorSymLinks(pid, mountPoint) // check for open files
199 || CheckFileMaps(pid, mountPoint) // check for mmap()
200 || CheckSymLink(pid, mountPoint, "cwd", "working directory") // check working directory
201 || CheckSymLink(pid, mountPoint, "root", "chroot") // check for chroot()
202 || CheckSymLink(pid, mountPoint, "exe", "executable path") // check executable path
203 )
204 {
205 int i;
206 int hit = 0;
207
208 for (i = 0; i < num_excluded; i++) {
209 if (pid == excluded[i]) {
210 LOGE("I just need a little more TIME captain!");
211 hit = 1;
212 break;
213 }
214 }
215
216 if (!hit) {
217 if (!sigkill) {
218 kill(pid, SIGTERM);
219 } else {
220 // Log this as an error since the app should
221 // have released it's resources long before
222 // this point.
223
224 LOGE("Sending SIGKILL to process %d", pid);
225 kill(pid, SIGKILL);
226 }
227 }
228 }
229 }
230
231 closedir(dir);
232}