0
|
1 /*
|
|
2 * Copyright (C) 2007 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 "com_google_ase_Exec.h"
|
|
18
|
|
19 #include <errno.h>
|
|
20 #include <fcntl.h>
|
|
21 #include <stdlib.h>
|
|
22 #include <sys/ioctl.h>
|
|
23 #include <sys/types.h>
|
|
24 #include <sys/wait.h>
|
|
25 #include <termios.h>
|
|
26 #include <unistd.h>
|
|
27
|
|
28 #include "android/log.h"
|
|
29
|
|
30 #define LOG_TAG "Exec"
|
|
31 #define LOG(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
|
32
|
|
33 void JNU_ThrowByName(JNIEnv * env, const char *name, const char *msg) {
|
|
34 jclass clazz = env->FindClass(name);
|
|
35 if (clazz != NULL) {
|
|
36 env->ThrowNew(clazz, msg);
|
|
37 }
|
|
38 env->DeleteLocalRef(clazz);
|
|
39 }
|
|
40
|
|
41 char *JNU_GetStringNativeChars(JNIEnv * env, jstring jstr) {
|
|
42 if (jstr == NULL) {
|
|
43 return NULL;
|
|
44 }
|
|
45 jbyteArray bytes = 0;
|
|
46 jthrowable exc;
|
|
47 char *result = 0;
|
|
48 if (env->EnsureLocalCapacity(2) < 0) {
|
|
49 return 0; /* out of memory error */
|
|
50 }
|
|
51 jclass Class_java_lang_String = env->FindClass("java/lang/String");
|
|
52 jmethodID MID_String_getBytes = env->GetMethodID(Class_java_lang_String, "getBytes", "()[B");
|
|
53 bytes = (jbyteArray) env->CallObjectMethod(jstr, MID_String_getBytes);
|
|
54 exc = env->ExceptionOccurred();
|
|
55 if (!exc) {
|
|
56 jint len = env->GetArrayLength(bytes);
|
|
57 result = (char *) malloc(len + 1);
|
|
58 if (result == 0) {
|
|
59 JNU_ThrowByName(env, "java/lang/OutOfMemoryError", 0);
|
|
60 env->DeleteLocalRef(bytes);
|
|
61 return 0;
|
|
62 }
|
|
63 env->GetByteArrayRegion(bytes, 0, len, (jbyte *) result);
|
|
64 result[len] = 0; /* NULL-terminate */
|
|
65 } else {
|
|
66 env->DeleteLocalRef(exc);
|
|
67 }
|
|
68 env->DeleteLocalRef(bytes);
|
|
69 return result;
|
|
70 }
|
|
71
|
|
72 int jniGetFDFromFileDescriptor(JNIEnv * env, jobject fileDescriptor) {
|
|
73 jclass Class_java_io_FileDescriptor = env->FindClass("java/io/FileDescriptor");
|
|
74 jfieldID descriptor = env->GetFieldID(Class_java_io_FileDescriptor,
|
|
75 "descriptor", "I");
|
|
76 return env->GetIntField(fileDescriptor, descriptor);
|
|
77 }
|
|
78
|
|
79 static int create_subprocess(const char *cmd, const char *arg0, const char *arg1, int *pProcessId) {
|
|
80 char *devname;
|
|
81 int ptm;
|
|
82 pid_t pid;
|
|
83
|
|
84 ptm = open("/dev/ptmx", O_RDWR); // | O_NOCTTY);
|
|
85 if (ptm < 0) {
|
|
86 LOG("[ cannot open /dev/ptmx - %s ]\n", strerror(errno));
|
|
87 return -1;
|
|
88 }
|
|
89 fcntl(ptm, F_SETFD, FD_CLOEXEC);
|
|
90
|
|
91 if (grantpt(ptm) || unlockpt(ptm) || ((devname = (char *) ptsname(ptm)) == 0)) {
|
|
92 LOG("[ trouble with /dev/ptmx - %s ]\n", strerror(errno));
|
|
93 return -1;
|
|
94 }
|
|
95
|
|
96 pid = fork();
|
|
97 if (pid < 0) {
|
|
98 LOG("- fork failed: %s -\n", strerror(errno));
|
|
99 return -1;
|
|
100 }
|
|
101
|
|
102 if (pid == 0) {
|
|
103 int pts;
|
|
104
|
|
105 setsid();
|
|
106
|
|
107 pts = open(devname, O_RDWR);
|
|
108 if (pts < 0)
|
|
109 exit(-1);
|
|
110
|
|
111 dup2(pts, 0);
|
|
112 dup2(pts, 1);
|
|
113 dup2(pts, 2);
|
|
114
|
|
115 close(ptm);
|
|
116
|
|
117 execl(cmd, cmd, arg0, arg1, NULL);
|
|
118 exit(-1);
|
|
119 } else {
|
|
120 *pProcessId = (int) pid;
|
|
121 return ptm;
|
|
122 }
|
|
123 }
|
|
124
|
|
125 JNIEXPORT jobject JNICALL Java_com_google_ase_Exec_createSubprocess(JNIEnv * env, jclass clazz,
|
|
126 jstring cmd, jstring arg0,
|
|
127 jstring arg1,
|
|
128 jintArray processIdArray) {
|
|
129 char *cmd_8 = JNU_GetStringNativeChars(env, cmd);
|
|
130 char *arg0_8 = JNU_GetStringNativeChars(env, arg0);
|
|
131 char *arg1_8 = JNU_GetStringNativeChars(env, arg1);
|
|
132
|
|
133 int procId;
|
|
134 int ptm = create_subprocess(cmd_8, arg0_8, arg1_8, &procId);
|
|
135
|
|
136 if (processIdArray) {
|
|
137 int procIdLen = env->GetArrayLength(processIdArray);
|
|
138 if (procIdLen > 0) {
|
|
139 jboolean isCopy;
|
|
140 int *pProcId = (int *) env->GetPrimitiveArrayCritical(processIdArray, &isCopy);
|
|
141 if (pProcId) {
|
|
142 *pProcId = procId;
|
|
143 env->ReleasePrimitiveArrayCritical(processIdArray, pProcId, 0);
|
|
144 }
|
|
145 }
|
|
146 }
|
|
147
|
|
148 jclass Class_java_io_FileDescriptor = env->FindClass("java/io/FileDescriptor");
|
|
149 jmethodID init = env->GetMethodID(Class_java_io_FileDescriptor,
|
|
150 "<init>", "()V");
|
|
151 jobject result = env->NewObject(Class_java_io_FileDescriptor, init);
|
|
152
|
|
153 if (!result) {
|
|
154 LOG("Couldn't create a FileDescriptor.");
|
|
155 } else {
|
|
156 jfieldID descriptor = env->GetFieldID(Class_java_io_FileDescriptor,
|
|
157 "descriptor", "I");
|
|
158 env->SetIntField(result, descriptor, ptm);
|
|
159 }
|
|
160
|
|
161 return result;
|
|
162 }
|
|
163
|
|
164 JNIEXPORT void Java_com_google_ase_Exec_setPtyWindowSize(JNIEnv * env, jclass clazz,
|
|
165 jobject fileDescriptor, jint row, jint col,
|
|
166 jint xpixel, jint ypixel) {
|
|
167 int fd;
|
|
168 struct winsize sz;
|
|
169
|
|
170 fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
|
|
171
|
|
172 if (env->ExceptionOccurred() != NULL) {
|
|
173 return;
|
|
174 }
|
|
175
|
|
176 sz.ws_row = row;
|
|
177 sz.ws_col = col;
|
|
178 sz.ws_xpixel = xpixel;
|
|
179 sz.ws_ypixel = ypixel;
|
|
180
|
|
181 ioctl(fd, TIOCSWINSZ, &sz);
|
|
182 }
|
|
183
|
|
184 JNIEXPORT jint Java_com_google_ase_Exec_waitFor(JNIEnv * env, jclass clazz, jint procId) {
|
|
185 int status;
|
|
186 waitpid(procId, &status, 0);
|
|
187 int result = 0;
|
|
188 if (WIFEXITED(status)) {
|
|
189 result = WEXITSTATUS(status);
|
|
190 }
|
|
191 return result;
|
|
192 }
|