• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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