• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 /*
18  * Copyright (C) 2007 The Android Open Source Project
19  *
20  * Licensed under the Apache License, Version 2.0 (the "License");
21  * you may not use this file except in compliance with the License.
22  * You may obtain a copy of the License at
23  *
24  *      http://www.apache.org/licenses/LICENSE-2.0
25  *
26  * Unless required by applicable law or agreed to in writing, software
27  * distributed under the License is distributed on an "AS IS" BASIS,
28  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29  * See the License for the specific language governing permissions and
30  * limitations under the License.
31  */
32 
33 #define LOG_TAG "Exec"
34 
35 #include "jni.h"
36 #include "utils/Log.h"
37 #include "utils/misc.h"
38 #include "android_runtime/AndroidRuntime.h"
39 
40 #include <sys/types.h>
41 #include <sys/ioctl.h>
42 #include <sys/wait.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <stdlib.h>
46 #include <unistd.h>
47 #include <termios.h>
48 
49 static jclass class_fileDescriptor;
50 static jfieldID field_fileDescriptor_descriptor;
51 static jmethodID method_fileDescriptor_init;
52 
53 
54 class String8 {
55 public:
String8()56     String8() {
57         mString = 0;
58     }
59 
~String8()60     ~String8() {
61         if (mString) {
62             free(mString);
63         }
64     }
65 
set(const char16_t * o,size_t numChars)66     void set(const char16_t* o, size_t numChars) {
67         mString = (char*) malloc(numChars + 1);
68         for (size_t i = 0; i < numChars; i++) {
69             mString[i] = (char) o[i];
70         }
71         mString[numChars] = '\0';
72     }
73 
string()74     const char* string() {
75         return mString;
76     }
77 private:
78     char* mString;
79 };
80 
create_subprocess(const char * cmd,const char * arg0,const char * arg1,int * pProcessId)81 static int create_subprocess(const char *cmd, const char *arg0, const char *arg1,
82     int* pProcessId)
83 {
84     char *devname;
85     int ptm;
86     pid_t pid;
87 
88     ptm = open("/dev/ptmx", O_RDWR); // | O_NOCTTY);
89     if(ptm < 0){
90         LOGE("[ cannot open /dev/ptmx - %s ]\n",strerror(errno));
91         return -1;
92     }
93     fcntl(ptm, F_SETFD, FD_CLOEXEC);
94 
95     if(grantpt(ptm) || unlockpt(ptm) ||
96        ((devname = (char*) ptsname(ptm)) == 0)){
97         LOGE("[ trouble with /dev/ptmx - %s ]\n", strerror(errno));
98         return -1;
99     }
100 
101     pid = fork();
102     if(pid < 0) {
103         LOGE("- fork failed: %s -\n", strerror(errno));
104         return -1;
105     }
106 
107     if(pid == 0){
108         close(ptm);
109 
110         int pts;
111 
112         setsid();
113 
114         pts = open(devname, O_RDWR);
115         if(pts < 0) exit(-1);
116 
117         dup2(pts, 0);
118         dup2(pts, 1);
119         dup2(pts, 2);
120 
121         execl(cmd, cmd, arg0, arg1, NULL);
122         exit(-1);
123     } else {
124         *pProcessId = (int) pid;
125         return ptm;
126     }
127 }
128 
129 
android_os_Exec_createSubProcess(JNIEnv * env,jobject clazz,jstring cmd,jstring arg0,jstring arg1,jintArray processIdArray)130 static jobject android_os_Exec_createSubProcess(JNIEnv *env, jobject clazz,
131     jstring cmd, jstring arg0, jstring arg1, jintArray processIdArray)
132 {
133     const jchar* str = cmd ? env->GetStringCritical(cmd, 0) : 0;
134     String8 cmd_8;
135     if (str) {
136         cmd_8.set(str, env->GetStringLength(cmd));
137         env->ReleaseStringCritical(cmd, str);
138     }
139 
140     str = arg0 ? env->GetStringCritical(arg0, 0) : 0;
141     const char* arg0Str = 0;
142     String8 arg0_8;
143     if (str) {
144         arg0_8.set(str, env->GetStringLength(arg0));
145         env->ReleaseStringCritical(arg0, str);
146         arg0Str = arg0_8.string();
147     }
148 
149     str = arg1 ? env->GetStringCritical(arg1, 0) : 0;
150     const char* arg1Str = 0;
151     String8 arg1_8;
152     if (str) {
153         arg1_8.set(str, env->GetStringLength(arg1));
154         env->ReleaseStringCritical(arg1, str);
155         arg1Str = arg1_8.string();
156     }
157 
158     int procId;
159     int ptm = create_subprocess(cmd_8.string(), arg0Str, arg1Str, &procId);
160 
161     if (processIdArray) {
162         int procIdLen = env->GetArrayLength(processIdArray);
163         if (procIdLen > 0) {
164             jboolean isCopy;
165 
166             int* pProcId = (int*) env->GetPrimitiveArrayCritical(processIdArray, &isCopy);
167             if (pProcId) {
168                 *pProcId = procId;
169                 env->ReleasePrimitiveArrayCritical(processIdArray, pProcId, 0);
170             }
171         }
172     }
173 
174     jobject result = env->NewObject(class_fileDescriptor, method_fileDescriptor_init);
175 
176     if (!result) {
177         LOGE("Couldn't create a FileDescriptor.");
178     }
179     else {
180         env->SetIntField(result, field_fileDescriptor_descriptor, ptm);
181     }
182 
183     return result;
184 }
185 
186 
android_os_Exec_setPtyWindowSize(JNIEnv * env,jobject clazz,jobject fileDescriptor,jint row,jint col,jint xpixel,jint ypixel)187 static void android_os_Exec_setPtyWindowSize(JNIEnv *env, jobject clazz,
188     jobject fileDescriptor, jint row, jint col, jint xpixel, jint ypixel)
189 {
190     int fd;
191     struct winsize sz;
192 
193     fd = env->GetIntField(fileDescriptor, field_fileDescriptor_descriptor);
194 
195     if (env->ExceptionOccurred() != NULL) {
196         return;
197     }
198 
199     sz.ws_row = row;
200     sz.ws_col = col;
201     sz.ws_xpixel = xpixel;
202     sz.ws_ypixel = ypixel;
203 
204     ioctl(fd, TIOCSWINSZ, &sz);
205 }
206 
android_os_Exec_waitFor(JNIEnv * env,jobject clazz,jint procId)207 static int android_os_Exec_waitFor(JNIEnv *env, jobject clazz,
208     jint procId) {
209     int status;
210     waitpid(procId, &status, 0);
211     int result = 0;
212     if (WIFEXITED(status)) {
213         result = WEXITSTATUS(status);
214     }
215     return result;
216 }
217 
android_os_Exec_close(JNIEnv * env,jobject clazz,jobject fileDescriptor)218 static void android_os_Exec_close(JNIEnv *env, jobject clazz, jobject fileDescriptor)
219 {
220     int fd;
221     struct winsize sz;
222 
223     fd = env->GetIntField(fileDescriptor, field_fileDescriptor_descriptor);
224 
225     if (env->ExceptionOccurred() != NULL) {
226         return;
227     }
228 
229     close(fd);
230 }
231 
232 
register_FileDescriptor(JNIEnv * env)233 static int register_FileDescriptor(JNIEnv *env)
234 {
235     class_fileDescriptor = env->FindClass("java/io/FileDescriptor");
236 
237     if (class_fileDescriptor == NULL) {
238         LOGE("Can't find java/io/FileDescriptor");
239         return -1;
240     }
241 
242     field_fileDescriptor_descriptor = env->GetFieldID(class_fileDescriptor, "descriptor", "I");
243 
244     if (field_fileDescriptor_descriptor == NULL) {
245         LOGE("Can't find FileDescriptor.descriptor");
246         return -1;
247     }
248 
249     method_fileDescriptor_init = env->GetMethodID(class_fileDescriptor, "<init>", "()V");
250     if (method_fileDescriptor_init == NULL) {
251         LOGE("Can't find FileDescriptor.init");
252         return -1;
253      }
254      return 0;
255 }
256 
257 
258 static const char *classPathName = "com/android/term/Exec";
259 
260 static JNINativeMethod method_table[] = {
261     { "createSubprocess", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[I)Ljava/io/FileDescriptor;",
262         (void*) android_os_Exec_createSubProcess },
263     { "setPtyWindowSize", "(Ljava/io/FileDescriptor;IIII)V",
264         (void*) android_os_Exec_setPtyWindowSize},
265     { "waitFor", "(I)I",
266         (void*) android_os_Exec_waitFor},
267     { "close", "(Ljava/io/FileDescriptor;)V",
268         (void*) android_os_Exec_close}
269 };
270 
271 /*
272  * Register several native methods for one class.
273  */
registerNativeMethods(JNIEnv * env,const char * className,JNINativeMethod * gMethods,int numMethods)274 static int registerNativeMethods(JNIEnv* env, const char* className,
275     JNINativeMethod* gMethods, int numMethods)
276 {
277     jclass clazz;
278 
279     clazz = env->FindClass(className);
280     if (clazz == NULL) {
281         LOGE("Native registration unable to find class '%s'", className);
282         return JNI_FALSE;
283     }
284     if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
285         LOGE("RegisterNatives failed for '%s'", className);
286         return JNI_FALSE;
287     }
288 
289     return JNI_TRUE;
290 }
291 
292 /*
293  * Register native methods for all classes we know about.
294  *
295  * returns JNI_TRUE on success.
296  */
registerNatives(JNIEnv * env)297 static int registerNatives(JNIEnv* env)
298 {
299   if (!registerNativeMethods(env, classPathName, method_table,
300                  sizeof(method_table) / sizeof(method_table[0]))) {
301     return JNI_FALSE;
302   }
303 
304   return JNI_TRUE;
305 }
306 
307 
308 // ----------------------------------------------------------------------------
309 
310 /*
311  * This is called by the VM when the shared library is first loaded.
312  */
313 
314 typedef union {
315     JNIEnv* env;
316     void* venv;
317 } UnionJNIEnvToVoid;
318 
JNI_OnLoad(JavaVM * vm,void * reserved)319 jint JNI_OnLoad(JavaVM* vm, void* reserved) {
320     UnionJNIEnvToVoid uenv;
321     uenv.venv = NULL;
322     jint result = -1;
323     JNIEnv* env = NULL;
324 
325     LOGI("JNI_OnLoad");
326 
327     if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) {
328         LOGE("ERROR: GetEnv failed");
329         goto bail;
330     }
331     env = uenv.env;
332 
333     if ((result = register_FileDescriptor(env)) < 0) {
334         LOGE("ERROR: registerFileDescriptor failed");
335         goto bail;
336     }
337 
338     if (registerNatives(env) != JNI_TRUE) {
339         LOGE("ERROR: registerNatives failed");
340         goto bail;
341     }
342 
343     result = JNI_VERSION_1_4;
344 
345 bail:
346     return result;
347 }
348