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 
readSymLink(const char * path,char * link,size_t max)35 int 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 
pathMatchesMountPoint(const char * path,const char * mountPoint)52 int 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 
getProcessName(int pid,char * buffer,size_t max)66 void 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 | O_CLOEXEC);
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 
checkFileDescriptorSymLinks(int pid,const char * mountPoint)79 int Process::checkFileDescriptorSymLinks(int pid, const char *mountPoint) {
80     return checkFileDescriptorSymLinks(pid, mountPoint, NULL, 0);
81 }
82 
checkFileDescriptorSymLinks(int pid,const char * mountPoint,char * openFilename,size_t max)83 int 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                 strlcpy(openFilename, link, max);
114             }
115             closedir(dir);
116             return 1;
117         }
118     }
119 
120     closedir(dir);
121     return 0;
122 }
123 
checkFileMaps(int pid,const char * mountPoint)124 int Process::checkFileMaps(int pid, const char *mountPoint) {
125     return checkFileMaps(pid, mountPoint, NULL, 0);
126 }
127 
checkFileMaps(int pid,const char * mountPoint,char * openFilename,size_t max)128 int 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                 strlcpy(openFilename, path, max);
144             }
145             fclose(file);
146             return 1;
147         }
148     }
149 
150     fclose(file);
151     return 0;
152 }
153 
checkSymLink(int pid,const char * mountPoint,const char * name)154 int 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 
getPid(const char * s)164 int 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 
vold_killProcessesWithOpenFiles(const char * path,int signal)173 extern "C" void vold_killProcessesWithOpenFiles(const char *path, int signal) {
174 	Process::killProcessesWithOpenFiles(path, signal);
175 }
176 
177 /*
178  * Hunt down processes that have files open at the given mount point.
179  */
killProcessesWithOpenFiles(const char * path,int signal)180 int Process::killProcessesWithOpenFiles(const char *path, int signal) {
181     int count = 0;
182     DIR* dir;
183     struct dirent* de;
184 
185     if (!(dir = opendir("/proc"))) {
186         SLOGE("opendir failed (%s)", strerror(errno));
187         return count;
188     }
189 
190     while ((de = readdir(dir))) {
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 
214         if (signal != 0) {
215             SLOGW("Sending %s to process %d", strsignal(signal), pid);
216             kill(pid, signal);
217             count++;
218         }
219     }
220     closedir(dir);
221     return count;
222 }
223