1 //
2 // Copyright (C) 2017 Google, Inc.
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_googlecode_android_scripting_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 #include <stdio.h>
28 #include <string.h>
29
30 #include "android/log.h"
31
32 #define LOG_TAG "Exec"
33 #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
34
CreateSubprocess(const char * cmd,char * args[],char * vars[],char * wkdir,pid_t * pid)35 int CreateSubprocess(const char* cmd, char* args[], char* vars[], char *wkdir, pid_t* pid) {
36 char* devname;
37 int ptm = open("/dev/ptmx", O_RDWR);
38 if(ptm < 0){
39 LOGE("Cannot open /dev/ptmx: %s\n", strerror(errno));
40 return -1;
41 }
42 fcntl(ptm, F_SETFD, FD_CLOEXEC);
43
44 if (grantpt(ptm) || unlockpt(ptm) ||
45 ((devname = (char*) ptsname(ptm)) == 0)) {
46 LOGE("Trouble with /dev/ptmx: %s\n", strerror(errno));
47 return -1;
48 }
49
50 *pid = fork();
51 if(*pid < 0) {
52 LOGE("Fork failed: %s\n", strerror(errno));
53 return -1;
54 }
55
56 if(*pid == 0){
57 int pts;
58 setsid();
59 pts = open(devname, O_RDWR);
60 if(pts < 0) {
61 exit(-1);
62 }
63 dup2(pts, 0);
64 dup2(pts, 1);
65 dup2(pts, 2);
66 close(ptm);
67 if (wkdir) chdir(wkdir);
68 execve(cmd, args, vars);
69 exit(-1);
70 } else {
71 return ptm;
72 }
73 }
74
JNU_ThrowByName(JNIEnv * env,const char * name,const char * msg)75 void JNU_ThrowByName(JNIEnv* env, const char* name, const char* msg) {
76 jclass clazz = env->FindClass(name);
77 if (clazz != NULL) {
78 env->ThrowNew(clazz, msg);
79 }
80 env->DeleteLocalRef(clazz);
81 }
82
JNU_GetStringNativeChars(JNIEnv * env,jstring jstr)83 char* JNU_GetStringNativeChars(JNIEnv* env, jstring jstr) {
84 if (jstr == NULL) {
85 return NULL;
86 }
87 jbyteArray bytes = 0;
88 jthrowable exc;
89 char* result = 0;
90 if (env->EnsureLocalCapacity(2) < 0) {
91 return 0; /* out of memory error */
92 }
93 jclass Class_java_lang_String = env->FindClass("java/lang/String");
94 jmethodID MID_String_getBytes = env->GetMethodID(
95 Class_java_lang_String, "getBytes", "()[B");
96 bytes = (jbyteArray) env->CallObjectMethod(jstr, MID_String_getBytes);
97 exc = env->ExceptionOccurred();
98 if (!exc) {
99 jint len = env->GetArrayLength(bytes);
100 result = (char*) malloc(len + 1);
101 if (result == 0) {
102 JNU_ThrowByName(env, "java/lang/OutOfMemoryError", 0);
103 env->DeleteLocalRef(bytes);
104 return 0;
105 }
106 env->GetByteArrayRegion(bytes, 0, len, (jbyte*) result);
107 result[len] = 0; /* NULL-terminate */
108 } else {
109 env->DeleteLocalRef(exc);
110 }
111 env->DeleteLocalRef(bytes);
112 return result;
113 }
114
JNU_GetFdFromFileDescriptor(JNIEnv * env,jobject fileDescriptor)115 int JNU_GetFdFromFileDescriptor(JNIEnv* env, jobject fileDescriptor) {
116 jclass Class_java_io_FileDescriptor = env->FindClass("java/io/FileDescriptor");
117 jfieldID descriptor = env->GetFieldID(Class_java_io_FileDescriptor, "descriptor", "I");
118 return env->GetIntField(fileDescriptor, descriptor);
119 }
120
Java_com_googlecode_android_1scripting_Exec_createSubprocess(JNIEnv * env,jclass clazz,jstring cmd,jobjectArray argArray,jobjectArray varArray,jstring workingDirectory,jintArray processIdArray)121 JNIEXPORT jobject JNICALL Java_com_googlecode_android_1scripting_Exec_createSubprocess(
122 JNIEnv* env, jclass clazz, jstring cmd, jobjectArray argArray, jobjectArray varArray,
123 jstring workingDirectory,
124 jintArray processIdArray) {
125 char* cmd_native = JNU_GetStringNativeChars(env, cmd);
126 char* wkdir_native = JNU_GetStringNativeChars(env, workingDirectory);
127 pid_t pid;
128 jsize len = 0;
129 if (argArray) {
130 len = env->GetArrayLength(argArray);
131 }
132 char* args[len + 2];
133 args[0] = cmd_native;
134 for (int i = 0; i < len; i++) {
135 jstring arg = (jstring) env->GetObjectArrayElement(argArray, i);
136 char* arg_native = JNU_GetStringNativeChars(env, arg);
137 args[i + 1] = arg_native;
138 }
139 args[len + 1] = NULL;
140
141 len = 0;
142 if (varArray) {
143 len = env->GetArrayLength(varArray);
144 }
145 char* vars[len + 1];
146 for (int i = 0; i < len; i++) {
147 jstring var = (jstring) env->GetObjectArrayElement(varArray, i);
148 char* var_native = JNU_GetStringNativeChars(env, var);
149 vars[i] = var_native;
150 }
151 vars[len] = NULL;
152
153 int ptm = CreateSubprocess(cmd_native, args, vars, wkdir_native, &pid);
154 if (processIdArray) {
155 if (env->GetArrayLength(processIdArray) > 0) {
156 jboolean isCopy;
157 int* proccessId = (int*) env->GetPrimitiveArrayCritical(processIdArray, &isCopy);
158 if (proccessId) {
159 *proccessId = (int) pid;
160 env->ReleasePrimitiveArrayCritical(processIdArray, proccessId, 0);
161 }
162 }
163 }
164
165 jclass Class_java_io_FileDescriptor =
166 env->FindClass("java/io/FileDescriptor");
167 jmethodID init = env->GetMethodID(Class_java_io_FileDescriptor, "<init>", "()V");
168 jobject result = env->NewObject(Class_java_io_FileDescriptor, init);
169
170 if (!result) {
171 LOGE("Couldn't create a FileDescriptor.");
172 } else {
173 jfieldID descriptor = env->GetFieldID(Class_java_io_FileDescriptor, "descriptor", "I");
174 env->SetIntField(result, descriptor, ptm);
175 }
176 return result;
177 }
178
Java_com_googlecode_android_1scripting_Exec_setPtyWindowSize(JNIEnv * env,jclass clazz,jobject fileDescriptor,jint row,jint col,jint xpixel,jint ypixel)179 JNIEXPORT void JNICALL Java_com_googlecode_android_1scripting_Exec_setPtyWindowSize(
180 JNIEnv* env, jclass clazz, jobject fileDescriptor, jint row, jint col, jint xpixel,
181 jint ypixel) {
182 struct winsize sz;
183 int fd = JNU_GetFdFromFileDescriptor(env, fileDescriptor);
184 if (env->ExceptionOccurred() != NULL) {
185 return;
186 }
187 sz.ws_row = row;
188 sz.ws_col = col;
189 sz.ws_xpixel = xpixel;
190 sz.ws_ypixel = ypixel;
191 ioctl(fd, TIOCSWINSZ, &sz);
192 }
193
Java_com_googlecode_android_1scripting_Exec_waitFor(JNIEnv * env,jclass clazz,jint procId)194 JNIEXPORT jint JNICALL Java_com_googlecode_android_1scripting_Exec_waitFor(JNIEnv* env, jclass clazz, jint procId) {
195 int status;
196 waitpid(procId, &status, 0);
197 int result = 0;
198 if (WIFEXITED(status)) {
199 result = WEXITSTATUS(status);
200 }
201 return result;
202 }
203