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