blob: f1d7146b02015de1abe2f92d0bb7cb3a6da09ef1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013, 2016 Wind River Systems, Inc. and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
#include "org_eclipse_cdt_utils_pty_PTY.h"
#include "org_eclipse_cdt_utils_pty_PTYInputStream.h"
#include "org_eclipse_cdt_utils_pty_PTYOutputStream.h"
#include "winpty.h"
#include <string>
#include <vector>
#include <map>
#include <stdlib.h>
#include <assert.h>
#include <ctime>
static std::map<int, winpty_t*> fd2pty;
static std::map<int, int> fd2rc;
/*
* Class: org_eclipse_cdt_utils_pty_PTY
* Method: openMaster
* Signature: (Z)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_org_eclipse_cdt_utils_pty_PTY_openMaster(JNIEnv *env, jobject jobj, jboolean console) {
jfieldID fid; /* Store the field ID */
jstring jstr = NULL;
jclass cls;
int master = -1;
char line[1024];
line[0] = '\0';
/* Open new winpty handle */
winpty_t *winpty = winpty_open(80, 40);
if (winpty == NULL) {
return NULL;
}
/* Configure console mode */
if (console) {
winpty_set_console_mode(winpty, 1);
}
/* Generate masterFD based on current system time */
srand((unsigned int) time(NULL));
master = rand();
/* Make sure masterFD does not exist */
while (fd2pty.find(master) != fd2pty.end())
master++;
sprintf(line, "winpty_%i", master);
/* Remember the winpty handle for the generated masterFD */
fd2pty.insert(std::pair<int, winpty_t*>(master, winpty));
/* Get a reference to the obj's class */
cls = env->GetObjectClass(jobj);
/* Set the master fd. */
fid = env->GetFieldID(cls, "master", "I");
if (fid == NULL) {
return NULL;
}
env->SetIntField(jobj, fid, (jint) master);
/* Create a new String for the slave. */
jstr = env->NewStringUTF(line);
return jstr;
}
/*
* Class: org_eclipse_cdt_utils_pty_PTY
* Method: change_window_size
* Signature: (III)I
*/
JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTY_change_1window_1size(JNIEnv *env, jobject jobj, jint fdm,
jint width, jint height) {
int fd;
std::map<int, winpty_t*>::const_iterator fd2pty_Iter;
fd = fdm;
fd2pty_Iter = fd2pty.find(fd);
if (fd2pty_Iter != fd2pty.end()) {
winpty_t *winpty = fd2pty_Iter->second;
if (winpty != NULL)
return winpty_set_size(winpty, width, height);
}
return 0;
}
/*
* Class: org_eclipse_cdt_utils_pty_PTYInputStream
* Method: read0
* Signature: (I[BI)I
*/
JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTYInputStream_read0(JNIEnv *env, jobject jobj, jint jfd,
jbyteArray buf, jint buf_len) {
DWORD amount = -1;
OVERLAPPED over;
int fd;
std::map<int, winpty_t*>::const_iterator fd2pty_Iter;
fd = jfd;
fd2pty_Iter = fd2pty.find(fd);
if (fd2pty_Iter != fd2pty.end()) {
winpty_t *winpty = fd2pty_Iter->second;
if (winpty != NULL) {
/* Get the pipe handle */
HANDLE handle = winpty_get_data_pipe(winpty);
memset(&over, 0, sizeof(over));
over.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
char *buffer = new char[buf_len];
memset(buffer, 0, sizeof(*buffer));
jbyte *data = env->GetByteArrayElements(buf, 0);
memset(data, 0, sizeof(*data));
amount = 0;
BOOL ret = ReadFile(handle, buffer, buf_len, &amount, &over);
if (!ret) {
DWORD error = GetLastError();
if (error == ERROR_IO_PENDING)
ret = GetOverlappedResult(handle, &over, &amount, TRUE);
}
if (ret && amount > 0)
memcpy(data, buffer, amount);
if (!ret || amount == 0)
amount = -1;
if (!ret && fd2pty.find(fd) != fd2pty.end()) {
int rc = winpty_get_exit_code(winpty);
fd2rc.insert(std::pair<int, int>(fd, rc));
}
delete[] buffer;
env->ReleaseByteArrayElements(buf, data, 0);
ResetEvent(over.hEvent);
}
}
return amount;
}
/*
* Class: org_eclipse_cdt_utils_pty_PTYInputStream
* Method: close0
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTYInputStream_close0(JNIEnv *env, jobject jobj, jint jfd) {
int fd;
std::map<int, winpty_t*>::iterator fd2pty_Iter;
fd = jfd;
fd2pty_Iter = fd2pty.find(fd);
if (fd2pty_Iter != fd2pty.end()) {
winpty_t *winpty = fd2pty_Iter->second;
fd2pty.erase(fd2pty_Iter);
if (winpty != NULL) {
winpty_close(winpty);
winpty = NULL;
}
}
return 0;
}
/*
* Class: org_eclipse_cdt_utils_pty_PTYOutputStream
* Method: write0
* Signature: (I[BI)I
*/
JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTYOutputStream_write0(JNIEnv *env, jobject jobj, jint jfd,
jbyteArray buf, jint buf_len) {
DWORD written = -1;
OVERLAPPED over;
int fd;
std::map<int, winpty_t*>::iterator fd2pty_Iter;
fd = jfd;
fd2pty_Iter = fd2pty.find(fd);
if (fd2pty_Iter != fd2pty.end()) {
winpty_t *winpty = fd2pty_Iter->second;
if (winpty != NULL) {
/* Get the pipe handle */
HANDLE handle = winpty_get_data_pipe(winpty);
memset(&over, 0, sizeof(over));
over.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
char *buffer = new char[buf_len];
memset(buffer, 0, sizeof(*buffer));
jbyte *data = env->GetByteArrayElements(buf, 0);
memcpy(buffer, data, buf_len);
BOOL ret = WriteFile(handle, buffer, buf_len, &written, &over);
env->ReleaseByteArrayElements(buf, data, 0);
if (!ret && GetLastError() == ERROR_IO_PENDING)
ret = GetOverlappedResult(handle, &over, &written, TRUE);
if (!ret || (int) written != buf_len)
written = -1;
delete[] buffer;
}
}
return written;
}
/*
* Class: org_eclipse_cdt_utils_pty_PTYOutputStream
* Method: close0
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTYOutputStream_close0(JNIEnv *env, jobject jobj, jint jfd) {
int fd;
std::map<int, winpty_t*>::iterator fd2pty_Iter;
fd = jfd;
fd2pty_Iter = fd2pty.find(fd);
if (fd2pty_Iter != fd2pty.end()) {
winpty_t *winpty = fd2pty_Iter->second;
fd2pty.erase(fd2pty_Iter);
if (winpty != NULL) {
winpty_close(winpty);
winpty = NULL;
}
}
return 0;
}
/*
* Convert convert slashes to backslashes.
*/
static std::wstring convertSlashes(const wchar_t *path) {
std::wstring ret;
for (int i = 0; path[i] != L'\0'; ++i) {
if (path[i] == L'/')
ret.push_back(L'\\');
else
ret.push_back(path[i]);
}
return ret;
}
// Convert argc/argv into a Win32 command-line following the escaping convention
// documented on MSDN. (e.g. see CommandLineToArgvW documentation)
static std::wstring argvToCommandLine(const std::vector<std::wstring> &argv) {
std::wstring result;
for (size_t argIndex = 0; argIndex < argv.size(); ++argIndex) {
if (argIndex > 0)
result.push_back(L' ');
const wchar_t *arg = argv[argIndex].c_str();
const bool quote = wcschr(arg, L' ') != NULL || wcschr(arg, L'\t') != NULL || *arg == L'\0';
if (quote)
result.push_back(L'\"');
int bsCount = 0;
for (const wchar_t *p = arg; *p != L'\0'; ++p) {
if (*p == L'\\') {
bsCount++;
} else if (*p == L'\"') {
result.append(bsCount * 2 + 1, L'\\');
result.push_back(L'\"');
bsCount = 0;
} else {
result.append(bsCount, L'\\');
bsCount = 0;
result.push_back(*p);
}
}
if (quote) {
result.append(bsCount * 2, L'\\');
result.push_back(L'\"');
} else {
result.append(bsCount, L'\\');
}
}
return result;
}
/*
* Class: org_eclipse_cdt_utils_pty_PTY
* Method: exec2
* Signature: ([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[ILjava/lang/String;IZ)I
*/
JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTY_exec2(JNIEnv *env, jobject jobj, jobjectArray jcmd,
jobjectArray jenv, jstring jdir, jobjectArray jchannels, jstring jslaveName, jint masterFD, jboolean console) {
int fd;
std::map<int, winpty_t*>::iterator fd2pty_Iter;
const wchar_t *cwdW = (const wchar_t*) env->GetStringChars(jdir, NULL);
const char *pts_name = env->GetStringUTFChars(jslaveName, NULL);
int pid = -1;
int i;
jint argc = env->GetArrayLength(jcmd);
jint envc = env->GetArrayLength(jenv);
if (jchannels == NULL || env->GetArrayLength(jchannels) != 3)
goto bail_out;
fd = masterFD;
fd2pty_Iter = fd2pty.find(fd);
if (fd2pty_Iter != fd2pty.end()) {
winpty_t *winpty = fd2pty_Iter->second;
if (winpty != NULL) {
std::vector < std::wstring > argVector;
for (i = 0; i < argc; i++) {
jstring j_str = (jstring) env->GetObjectArrayElement(jcmd, i);
const wchar_t *w_str = (const wchar_t*) env->GetStringChars(j_str, NULL);
if (i == 0)
argVector.push_back(convertSlashes(w_str));
else
argVector.push_back(w_str);
env->ReleaseStringChars(j_str, (const jchar*) w_str);
env->DeleteLocalRef(j_str);
}
std::wstring envp;
for (i = 0; i < envc; i++) {
jstring j_str = (jstring) env->GetObjectArrayElement(jenv, i);
const wchar_t *w_str = (const wchar_t*) env->GetStringChars(j_str, NULL);
envp.append(w_str);
envp.push_back(L'\0');
env->ReleaseStringChars(j_str, (const jchar*) w_str);
env->DeleteLocalRef(j_str);
}
std::wstring cmdLine = argvToCommandLine(argVector);
const wchar_t *cmdLineW = cmdLine.c_str();
int ret = winpty_start_process(winpty, NULL, cmdLineW, cwdW, envp.c_str());
if (ret == 0) {
// Success. Get the process id.
pid = winpty_get_process_id(winpty);
}
}
}
bail_out: env->ReleaseStringChars(jdir, (const jchar*) cwdW);
env->ReleaseStringUTFChars(jslaveName, pts_name);
return pid;
}
/*
* Class: org_eclipse_cdt_utils_pty_PTY
* Method: waitFor
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTY_waitFor(JNIEnv *env, jobject jobj, jint masterFD, jint pid) {
int status = -1;
DWORD flags;
int fd;
std::map<int, winpty_t*>::iterator fd2pty_Iter;
std::map<int, int>::iterator fd2rc_Iter;
fd = masterFD;
fd2pty_Iter = fd2pty.find(fd);
if (fd2pty_Iter != fd2pty.end()) {
winpty_t *winpty = fd2pty_Iter->second;
if (winpty != NULL) {
HANDLE handle = winpty_get_data_pipe(winpty);
BOOL success;
do {
success = GetHandleInformation(handle, &flags);
if (success)
Sleep(500);
} while (success);
fd2rc_Iter = fd2rc.find(fd);
if (fd2rc_Iter != fd2rc.end()) {
status = fd2rc_Iter->second;
fd2rc.erase(fd2rc_Iter);
}
}
}
return status;
}