diff options
author | Torbjörn Svensson | 2020-06-21 14:52:00 +0000 |
---|---|---|
committer | Jonah Graham | 2020-08-07 00:55:42 +0000 |
commit | 00a52086c9e3c7457be830ee38626c2af0225c23 (patch) | |
tree | 43dbb2af7ea6945ed40d567c48fe40b03da3d955 /core/org.eclipse.cdt.core.native | |
parent | 69acfe4819bcde20d20598ede0be1a6d33a560e3 (diff) | |
download | org.eclipse.cdt-00a52086c9e3c7457be830ee38626c2af0225c23.tar.gz org.eclipse.cdt-00a52086c9e3c7457be830ee38626c2af0225c23.tar.xz org.eclipse.cdt-00a52086c9e3c7457be830ee38626c2af0225c23.zip |
Bug 521515: Adopt native build support on jenkins
Change-Id: I6aee7a7c94f93375d3a2ddb0171602a27a6873e7
Signed-off-by: Torbjörn Svensson <azoff@svenskalinuxforeningen.se>
Diffstat (limited to 'core/org.eclipse.cdt.core.native')
24 files changed, 3875 insertions, 1 deletions
diff --git a/core/org.eclipse.cdt.core.native/build.properties b/core/org.eclipse.cdt.core.native/build.properties index 2e7a449116e..006a08c7bcb 100644 --- a/core/org.eclipse.cdt.core.native/build.properties +++ b/core/org.eclipse.cdt.core.native/build.properties @@ -22,7 +22,8 @@ bin.includes = plugin.properties,\ about.properties,\ plugin.xml src.includes = about.html,\ - schema/ + schema/,\ + native_src/ javadoc.packages = org.eclipse.cdt.utils.*,\ org.eclipse.cdt.utils.pty.*,\ org.eclipse.cdt.utils.spawner.* diff --git a/core/org.eclipse.cdt.core.native/native_src/Makefile b/core/org.eclipse.cdt.core.native/native_src/Makefile new file mode 100644 index 00000000000..36324b888bd --- /dev/null +++ b/core/org.eclipse.cdt.core.native/native_src/Makefile @@ -0,0 +1,140 @@ +#******************************************************************************* +# Copyright (c) 2002, 2015, 2020 QNX Software Systems 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: +# QNX Software Systems - initial API and implementation +# Torbjörn Svensson - Bug 521515 - Adopted jenkins build +#*******************************************************************************/ + +ifeq ($(JAVA_HOME),) +$(error JAVA_HOME not set in environment) +endif + +OS_DIR_WIN32_X86_64 := ../../org.eclipse.cdt.core.win32.x86_64/os/win32/x86_64 +OS_DIR_LINUX_X86_64 := ../../org.eclipse.cdt.core.linux.x86_64/os/linux/x86_64 +OS_DIR_LINUX_PPC64LE := ../../org.eclipse.cdt.core.linux.ppc64le/os/linux/ppc64le +OS_DIR_MACOS_X86_64 := ../../org.eclipse.cdt.core.macosx/os/macosx/x86_64 +OS_DIR_MACOS_X86 := ../../org.eclipse.cdt.core.macosx/os/macosx/x86 + + +UNAME = $(shell uname) +ifeq ($(UNAME),Linux) +LIBS = \ + $(OS_DIR_WIN32_X86_64)/starter.exe \ + $(OS_DIR_WIN32_X86_64)/spawner.dll \ + $(OS_DIR_LINUX_X86_64)/libspawner.so \ + $(OS_DIR_LINUX_X86_64)/libpty.so \ + $(OS_DIR_MACOS_X86_64)/libspawner.jnilib \ + $(OS_DIR_MACOS_X86_64)/libpty.jnilib \ + $(OS_DIR_MACOS_X86)/libspawner.jnilib \ + $(OS_DIR_MACOS_X86)/libpty.jnilib +else +ifeq ($(UNAME),Darwin) +LIBS = \ + $(OS_DIR_MACOS_X86_64)/libspawner.jnilib \ + $(OS_DIR_MACOS_X86_64)/libpty.jnilib \ + $(OS_DIR_MACOS_X86)/libspawner.jnilib \ + $(OS_DIR_MACOS_X86)/libpty.jnilib +else +LIBS = \ + $(OS_DIR_WIN32_X86_64)/starter.exe \ + $(OS_DIR_WIN32_X86_64)/spawner.dll +endif +endif + +all: $(LIBS) + +clean : + $(RM) $(LIBS) + +rebuild: clean all + + +# Windows x86_64 +# Windows DLLs have a build timestamp in them. This makes it impossible to have reproducible builds. +# However, x86_64-w64-mingw32-ld on Debian/Ubuntu has a patch that overrides the current date +# using the SOURCE_DATE_EPOCH environment variable. Therefore we set SOURCE_DATE_EPOCH to a +# consistent timestamp that can be reproduced. We base it off of the commit timestamp of the +# most recent git commit in this directory. +$(OS_DIR_WIN32_X86_64)/starter.exe: win/starter.c + mkdir -p $(dir $@) && \ + SOURCE_DATE_EPOCH=$(shell git log -1 --pretty=format:%ct -- .) \ + x86_64-w64-mingw32-gcc -o $@ -Iinclude -I"$(JAVA_HOME)/include" -I"$(JAVA_HOME)/include/win32" \ + -DUNICODE \ + win/starter.c \ + -lpsapi + +$(OS_DIR_WIN32_X86_64)/spawner.dll: win/iostream.c win/raise.c win/spawner.c win/Win32ProcessEx.c + mkdir -p $(dir $@) && \ + SOURCE_DATE_EPOCH=$(shell git log -1 --pretty=format:%ct -- .) \ + x86_64-w64-mingw32-gcc -o $@ -Iinclude -I"$(JAVA_HOME)/include" -I"$(JAVA_HOME)/include/win32" \ + -DUNICODE \ + win/iostream.c win/raise.c win/spawner.c win/Win32ProcessEx.c \ + -Wl,--kill-at --shared + +# Linux x86_64 +$(OS_DIR_LINUX_X86_64)/libspawner.so: unix/spawner.c unix/io.c unix/exec_unix.c unix/exec_pty.c unix/openpty.c unix/pfind.c + mkdir -p $(dir $@) && \ + gcc -m64 -o $@ -Wl,-soname,$(notdir $@) -Iinclude -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux -fpic \ + -D_REENTRANT -D_GNU_SOURCE \ + unix/spawner.c unix/io.c unix/exec_unix.c unix/exec_pty.c unix/openpty.c unix/pfind.c \ + -shared -lc + +$(OS_DIR_LINUX_X86_64)/libpty.so: unix/openpty.c unix/pty.c unix/ptyio.c + mkdir -p $(dir $@) && \ + gcc -m64 -o $@ -Wl,-soname,$(notdir $@) -Iinclude -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux -fpic \ + -D_REENTRANT -D_GNU_SOURCE \ + unix/openpty.c unix/pty.c unix/ptyio.c \ + -shared -lc + +# Linux ppc64le +$(OS_DIR_LINUX_PPC64LE)/libspawner.so: unix/spawner.c unix/io.c unix/exec_unix.c unix/exec_pty.c unix/openpty.c unix/pfind.c + mkdir -p $(dir $@) && \ + gcc -m64 -o $@ -Wl,-soname,$(notdir $@) -Iinclude -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux -fpic \ + -D_REENTRANT -D_GNU_SOURCE \ + unix/spawner.c unix/io.c unix/exec_unix.c unix/exec_pty.c unix/openpty.c unix/pfind.c \ + -shared -lc + +$(OS_DIR_LINUX_PPC64LE)/libpty.so: unix/openpty.c unix/pty.c unix/ptyio.c + mkdir -p $(dir $@) && \ + gcc -m64 -o $@ -Wl,-soname,$(notdir $@) -Iinclude -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux -fpic \ + -D_REENTRANT -D_GNU_SOURCE \ + unix/openpty.c unix/pty.c unix/ptyio.c \ + -shared -lc + +# macos x86_64 +$(OS_DIR_MACOS_X86_64)/libspawner.jnilib: unix/spawner.c unix/io.c unix/exec_unix.c unix/exec_pty.c unix/openpty.c unix/pfind.c + mkdir -p $(dir $@) && \ + x86_64-apple-darwin17-clang -o $@ -arch x86_64 -Iinclude -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/darwin -fPIC \ + -D_REENTRANT \ + unix/spawner.c unix/io.c unix/exec_unix.c unix/exec_pty.c unix/openpty.c unix/pfind.c \ + -dynamiclib -lc -framework JavaVM + +$(OS_DIR_MACOS_X86_64)/libpty.jnilib: unix/openpty.c unix/pty.c unix/ptyio.c + mkdir -p $(dir $@) && \ + x86_64-apple-darwin17-clang -o $@ -arch x86_64 -Iinclude -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/darwin -fPIC \ + -D_REENTRANT \ + unix/openpty.c unix/pty.c unix/ptyio.c \ + -dynamiclib -lc -framework JavaVM + +# macos x86 +$(OS_DIR_MACOS_X86)/libspawner.jnilib: unix/spawner.c unix/io.c unix/exec_unix.c unix/exec_pty.c unix/openpty.c unix/pfind.c + mkdir -p $(dir $@) && \ + x86_64-apple-darwin17-clang -o $@ -arch i386 -Iinclude -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/darwin -fPIC \ + -D_REENTRANT \ + unix/spawner.c unix/io.c unix/exec_unix.c unix/exec_pty.c unix/openpty.c unix/pfind.c \ + -dynamiclib -lc -framework JavaVM + +$(OS_DIR_MACOS_X86)/libpty.jnilib: unix/openpty.c unix/pty.c unix/ptyio.c + mkdir -p $(dir $@) && \ + x86_64-apple-darwin17-clang -o $@ -arch i386 -Iinclude -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/darwin -fPIC \ + -D_REENTRANT \ + unix/openpty.c unix/pty.c unix/ptyio.c \ + -dynamiclib -lc -framework JavaVM diff --git a/core/org.eclipse.cdt.core.native/native_src/include/PTY.h b/core/org.eclipse.cdt.core.native/native_src/include/PTY.h new file mode 100644 index 00000000000..d73d5c00e16 --- /dev/null +++ b/core/org.eclipse.cdt.core.native/native_src/include/PTY.h @@ -0,0 +1,45 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include <jni.h> +/* Header for class org_eclipse_cdt_utils_pty_PTY */ + +#ifndef _Included_org_eclipse_cdt_utils_pty_PTY +#define _Included_org_eclipse_cdt_utils_pty_PTY +#ifdef __cplusplus +extern "C" { +#endif +/* + * 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 *, jobject, jboolean); + +/* + * 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 *, jobject, jint, jint, jint); + +/* + * 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 *, jobject, jobjectArray, jobjectArray, jstring, jintArray, jstring, jint, jboolean); + +/* + * Class: org_eclipse_cdt_utils_pty_PTY + * Method: waitFor + * Signature: (II)I + */ +JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTY_waitFor + (JNIEnv *, jobject, jint, jint); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/core/org.eclipse.cdt.core.native/native_src/include/PTYInputStream.h b/core/org.eclipse.cdt.core.native/native_src/include/PTYInputStream.h new file mode 100644 index 00000000000..7767be6bd8c --- /dev/null +++ b/core/org.eclipse.cdt.core.native/native_src/include/PTYInputStream.h @@ -0,0 +1,31 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include <jni.h> +/* Header for class org_eclipse_cdt_utils_pty_PTYInputStream */ + +#ifndef _Included_org_eclipse_cdt_utils_pty_PTYInputStream +#define _Included_org_eclipse_cdt_utils_pty_PTYInputStream +#ifdef __cplusplus +extern "C" { +#endif +#undef org_eclipse_cdt_utils_pty_PTYInputStream_MAX_SKIP_BUFFER_SIZE +#define org_eclipse_cdt_utils_pty_PTYInputStream_MAX_SKIP_BUFFER_SIZE 2048L +/* + * 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 *, jobject, jint, jbyteArray, jint); + +/* + * Class: org_eclipse_cdt_utils_pty_PTYInputStream + * Method: close0 + * Signature: (I)I + */ +JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTYInputStream_close0 + (JNIEnv *, jobject, jint); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/core/org.eclipse.cdt.core.native/native_src/include/PTYOutputStream.h b/core/org.eclipse.cdt.core.native/native_src/include/PTYOutputStream.h new file mode 100644 index 00000000000..fb28491060e --- /dev/null +++ b/core/org.eclipse.cdt.core.native/native_src/include/PTYOutputStream.h @@ -0,0 +1,29 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include <jni.h> +/* Header for class org_eclipse_cdt_utils_pty_PTYOutputStream */ + +#ifndef _Included_org_eclipse_cdt_utils_pty_PTYOutputStream +#define _Included_org_eclipse_cdt_utils_pty_PTYOutputStream +#ifdef __cplusplus +extern "C" { +#endif +/* + * 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 *, jobject, jint, jbyteArray, jint); + +/* + * Class: org_eclipse_cdt_utils_pty_PTYOutputStream + * Method: close0 + * Signature: (I)I + */ +JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTYOutputStream_close0 + (JNIEnv *, jobject, jint); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/core/org.eclipse.cdt.core.native/native_src/include/Spawner.h b/core/org.eclipse.cdt.core.native/native_src/include/Spawner.h new file mode 100644 index 00000000000..10750f2e202 --- /dev/null +++ b/core/org.eclipse.cdt.core.native/native_src/include/Spawner.h @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2002, 2010 QNX Software Systems 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: + * QNX Software Systems - initial API and implementation + * + * Spawner.h + * + * This is a part of JNI implementation of spawner + *******************************************************************************/ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include <jni.h> +/* Header for class org_eclipse_cdt_utils_spawner_Spawner */ + +#ifndef _Included_org_eclipse_cdt_utils_spawner_Spawner +#define _Included_org_eclipse_cdt_utils_spawner_Spawner +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_eclipse_cdt_utils_spawner_Spawner + * Method: exec0 + * Signature: ([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[I)I + */ +JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec0 + (JNIEnv *, jobject, jobjectArray, jobjectArray, jstring, jintArray); + +/* + * Class: org_eclipse_cdt_utils_spawner_Spawner + * Method: exec1 + * Signature: ([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec1 + (JNIEnv *, jobject, jobjectArray, jobjectArray, jstring); + +/* + * Class: org_eclipse_cdt_utils_spawner_Spawner + * 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_spawner_Spawner_exec2 + (JNIEnv *, jobject, jobjectArray, jobjectArray, jstring, jintArray, jstring, jint, jboolean); + +/* + * Class: org_eclipse_cdt_utils_spawner_Spawner + * Method: raise + * Signature: (II)I + */ +JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_raise + (JNIEnv *, jobject, jint, jint); + +/* + * Class: org_eclipse_cdt_utils_spawner_Spawner + * Method: waitFor + * Signature: (I)I + */ +JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_waitFor + (JNIEnv *, jobject, jint); + +// #define DEBUG_MONITOR + +int interruptProcess(int pid); + + +#ifdef __cplusplus +} +#endif + +// #define DEBUG_MONITOR + +#endif diff --git a/core/org.eclipse.cdt.core.native/native_src/include/SpawnerInputStream.h b/core/org.eclipse.cdt.core.native/native_src/include/SpawnerInputStream.h new file mode 100644 index 00000000000..653c5659524 --- /dev/null +++ b/core/org.eclipse.cdt.core.native/native_src/include/SpawnerInputStream.h @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2002, 2007 QNX Software Systems 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: + * QNX Software Systems - initial API and implementation + * + * SpawnerInputStream.h + * + * This is a part of JNI implementation of spawner + *******************************************************************************/ + +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include <jni.h> +/* Header for class com_qnx_tools_utils_spawner_SpawnerInputStream */ + +#ifndef _Included_com_qnx_tools_utils_spawner_SpawnerInputStream +#define _Included_com_qnx_tools_utils_spawner_SpawnerInputStream +#ifdef __cplusplus +extern "C" { +#endif +#undef com_qnx_tools_utils_spawner_SpawnerInputStream_SKIP_BUFFER_SIZE +#define com_qnx_tools_utils_spawner_SpawnerInputStream_SKIP_BUFFER_SIZE 2048L +/* Inaccessible static: skipBuffer */ +/* + * Class: org_elipse_cdt_utils_spawner_SpawnerInputStream + * Method: read0 + * Signature: (I[BI)I + */ +JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerInputStream_read0 + (JNIEnv *, jobject, jint, jbyteArray, jint); + +/* + * Class: org_eclipse_cdt_utils_spawner_SpawnerInputStream + * Method: close0 + * Signature: (I)I + */ +JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerInputStream_close0 + (JNIEnv *, jobject, jint); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/core/org.eclipse.cdt.core.native/native_src/include/SpawnerOutputStream.h b/core/org.eclipse.cdt.core.native/native_src/include/SpawnerOutputStream.h new file mode 100644 index 00000000000..fb3e0354e88 --- /dev/null +++ b/core/org.eclipse.cdt.core.native/native_src/include/SpawnerOutputStream.h @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2002, 2007 QNX Software Systems 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: + * QNX Software Systems - initial API and implementation + * + * SpawnerOutputStream.h + * + * This is a part of JNI implementation of spawner + *******************************************************************************/ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include <jni.h> +/* Header for class com_qnx_tools_utils_spawner_SpawnerOutputStream */ + +#ifndef _Included_com_qnx_tools_utils_spawner_SpawnerOutputStream +#define _Included_com_qnx_tools_utils_spawner_SpawnerOutputStream +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_eclipse_cdt_utils_spawner_SpawnerOutputStream + * Method: write0 + * Signature: (I[BI)I + */ +JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerOutputStream_write0 + (JNIEnv *, jobject, jint, jbyteArray, jint); + +/* + * Class: org_eclipse_cdt_utils_spawner_SpawnerOutputStream + * Method: close0 + * Signature: (I)I + */ +JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerOutputStream_close0 + (JNIEnv *, jobject, jint); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/core/org.eclipse.cdt.core.native/native_src/unix/exec0.h b/core/org.eclipse.cdt.core.native/native_src/unix/exec0.h new file mode 100644 index 00000000000..04f363d5e53 --- /dev/null +++ b/core/org.eclipse.cdt.core.native/native_src/unix/exec0.h @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2002, 2010 QNX Software Systems 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: + * QNX Software Systems - initial API and implementation + * Wind River Systems, Inc. + * Mikhail Zabaluev (Nokia) - bug 82744 + *******************************************************************************/ +#include <unistd.h> +#include <sys/wait.h> +#include <sys/types.h> +#include <signal.h> +#include <errno.h> + +extern pid_t exec0(const char *path, char *const argv[], + char *const envp[], const char *dirpath, + int channels[3]); + + +extern pid_t exec_pty(const char *path, char *const argv[], + char *const envp[], const char *dirpath, + int channels[3], const char *pts_name, int fdm, + int console); + +extern int wait0(pid_t pid); diff --git a/core/org.eclipse.cdt.core.native/native_src/unix/exec_pty.c b/core/org.eclipse.cdt.core.native/native_src/unix/exec_pty.c new file mode 100644 index 00000000000..e020053db41 --- /dev/null +++ b/core/org.eclipse.cdt.core.native/native_src/unix/exec_pty.c @@ -0,0 +1,189 @@ +/******************************************************************************* + * Copyright (c) 2004, 2010 QNX Software Systems 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: + * QNX Software Systems - initial API and implementation + * Wind River Systems, Inc. + * Mikhail Zabaluev (Nokia) - bug 82744 + * Mikhail Sennikovsky - bug 145737 + *******************************************************************************/ +#include "exec0.h" +#include "openpty.h" +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <libgen.h> +#include <stdlib.h> +#include <termios.h> + +/* from pfind.c */ +extern char *pfind(const char *name, char * const envp[]); + +pid_t +exec_pty(const char *path, char *const argv[], char *const envp[], + const char *dirpath, int channels[3], const char *pts_name, int fdm, int console) +{ + int pipe2[2]; + pid_t childpid; + char *full_path; + + /* + * We use pfind() to check that the program exists and is an executable. + * If not pass the error up. Also execve() wants a full path. + */ + full_path = pfind(path, envp); + if (full_path == NULL) { + fprintf(stderr, "Unable to find full path for \"%s\"\n", (path) ? path : ""); + return -1; + } + + /* + * Make sure we can create our pipes before forking. + */ + if (channels != NULL && console) { + if (pipe(pipe2) < 0) { + fprintf(stderr, "%s(%d): returning due to error: %s\n", __FUNCTION__, __LINE__, strerror(errno)); + free(full_path); + return -1; + } + } + + childpid = fork(); + + if (childpid < 0) { + fprintf(stderr, "%s(%d): returning due to error: %s\n", __FUNCTION__, __LINE__, strerror(errno)); + free(full_path); + return -1; + } else if (childpid == 0) { /* child */ + + chdir(dirpath); + + if (channels != NULL) { + int fds; + + if (!console && setsid() < 0) { + perror("setsid()"); + return -1; + } + + fds = ptys_open(fdm, pts_name); + if (fds < 0) { + fprintf(stderr, "%s(%d): returning due to error: %s\n", __FUNCTION__, __LINE__, strerror(errno)); + return -1; + } + + /* Close the read end of pipe2 */ + if (console && close(pipe2[0]) == -1) { + perror("close(pipe2[0]))"); + } + + /* close the master, no need in the child */ + close(fdm); + + if (console) { + set_noecho(fds); + if (setpgid(getpid(), getpid()) < 0) { + perror("setpgid()"); + return -1; + } + } + + /* redirections */ + dup2(fds, STDIN_FILENO); /* dup stdin */ + dup2(fds, STDOUT_FILENO); /* dup stdout */ + if (console) { + dup2(pipe2[1], STDERR_FILENO); /* dup stderr */ + } else { + dup2(fds, STDERR_FILENO); /* dup stderr */ + } + close(fds); /* done with fds. */ + } + + /* Close all the fd's in the child */ + { + int fdlimit = sysconf(_SC_OPEN_MAX); + int fd = 3; + + while (fd < fdlimit) + close(fd++); + } + + if (envp[0] == NULL) { + execv(full_path, argv); + } else { + execve(full_path, argv, envp); + } + + _exit(127); + + } else if (childpid != 0) { /* parent */ + if (console) { + set_noecho(fdm); + } + if (channels != NULL) { + channels[0] = fdm; /* Input Stream. */ + channels[1] = fdm; /* Output Stream. */ + if (console) { + /* close the write end of pipe1 */ + if (close(pipe2[1]) == -1) + perror("close(pipe2[1])"); + channels[2] = pipe2[0]; /* stderr Stream. */ + } else { + channels[2] = fdm; /* Error Stream. */ + } + } + + free(full_path); + return childpid; + } + + free(full_path); + return -1; /*NOT REACHED */ +} +#ifdef __STAND_ALONE__ +int main(int argc, char **argv, char **envp) { + const char *path = "./bufferring_test"; + int channels[3] = { -1, -1, -1}; + int status; + FILE *app_stdin; + FILE *app_stdout; + FILE *app_stderr; + char pts_name[32]; + int fdm; + char buffer[32]; + + fdm = ptym_open(pts_name); + status = exec_pty(path, argv, envp, ".", channels, pts_name, fdm); + if (status >= 0) { + app_stdin = fdopen(channels[0], "w"); + app_stdout = fdopen(channels[1], "r"); + app_stderr = fdopen(channels[2], "r"); + if (app_stdout == NULL || app_stderr == NULL || app_stdin == NULL) { + fprintf(stderr, "PROBLEMS\n"); + } else { + fputs("foo\n", app_stdin); + fputs("bar\n", app_stdin); + while(fgets(buffer, sizeof buffer, app_stdout) != NULL) { + fprintf(stdout, "STDOUT: %s\n", buffer); + } + while(fgets(buffer, sizeof buffer, app_stderr) != NULL) { + fprintf(stdout, "STDERR: %s\n", buffer); + } + } + } + fputs("bye\n", stdout); + close(channels[0]); + close(channels[1]); + close(channels[2]); + return 0; +} +#endif diff --git a/core/org.eclipse.cdt.core.native/native_src/unix/exec_unix.c b/core/org.eclipse.cdt.core.native/native_src/unix/exec_unix.c new file mode 100644 index 00000000000..4feb805f15d --- /dev/null +++ b/core/org.eclipse.cdt.core.native/native_src/unix/exec_unix.c @@ -0,0 +1,161 @@ +/******************************************************************************* + * Copyright (c) 2002, 2010 QNX Software Systems 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: + * QNX Software Systems - initial API and implementation + * Wind River Systems, Inc. + * Mikhail Sennikovsky - bug 145737 + *******************************************************************************/ +#include "exec0.h" +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <libgen.h> +#include <stdlib.h> + +/* from pfind.c */ +extern char *pfind(const char *name, char * const envp[]); + +pid_t +exec0(const char *path, char *const argv[], char *const envp[], + const char *dirpath, int channels[3]) +{ + int pipe0[2], pipe1[2], pipe2[2]; + pid_t childpid; + char *full_path; + + /* + * We use pfind() to check that the program exists and is an executable. + * If not pass the error up. Also execve() wants a full path. + */ + full_path = pfind(path, envp); + if (full_path == NULL) { + fprintf(stderr, "Unable to find full path for \"%s\"\n", (path) ? path : ""); + return -1; + } + + /* + * Make sure we can create our pipes before forking. + */ + if (channels != NULL) { + if (pipe(pipe0) < 0 || pipe(pipe1) < 0 || pipe(pipe2) < 0) { + fprintf(stderr, "%s(%d): returning due to error.\n", + __FUNCTION__, __LINE__); + free(full_path); + return -1; + } + } + + childpid = fork(); + + if (childpid < 0) { + fprintf(stderr, "%s(%d): returning due to error: %s\n", + __FUNCTION__, __LINE__, strerror(errno)); + free(full_path); + return -1; + } else if (childpid == 0) { /* child */ + char *ptr; + + chdir(dirpath); + + if (channels != NULL) { + /* Close the write end of pipe0 */ + if (close(pipe0[1]) == -1) + perror("close(pipe0[1])"); + + /* Close the read end of pipe1 */ + if (close(pipe1[0]) == -1) + perror("close(pipe1[0])"); + + /* Close the read end of pipe2 */ + if (close(pipe2[0]) == -1) + perror("close(pipe2[0]))"); + + /* redirections */ + dup2(pipe0[0], STDIN_FILENO); /* dup stdin */ + dup2(pipe1[1], STDOUT_FILENO); /* dup stdout */ + dup2(pipe2[1], STDERR_FILENO); /* dup stderr */ + } + + /* Close all the fd's in the child */ + { + int fdlimit = sysconf(_SC_OPEN_MAX); + int fd = 3; + + while (fd < fdlimit) + close(fd++); + } + + setpgid(getpid(), getpid()); + + if (envp[0] == NULL) { + execv(full_path, argv); + } else { + execve(full_path, argv, envp); + } + + _exit(127); + + } else if (childpid != 0) { /* parent */ + + char b; + + if (channels != NULL) { + /* close the read end of pipe1 */ + if (close(pipe0[0]) == -1) + perror("close(pipe0[0])"); + + /* close the write end of pipe2 */ + if (close(pipe1[1]) == -1) + perror("close(pipe1[1])"); + + /* close the write end of pipe2 */ + if (close(pipe2[1]) == -1) + perror("close(pipe2[1])"); + + channels[0] = pipe0[1]; /* Output Stream. */ + channels[1] = pipe1[0]; /* Input Stream. */ + channels[2] = pipe2[0]; /* Input Stream. */ + } + + free(full_path); + return childpid; + } + + free(full_path); + return -1; /*NOT REACHED */ +} + + +int wait0(pid_t pid) +{ + int status; + int val = -1; + + if (pid < 0) + return -1; + + for (;;) { + if (waitpid(pid, &status, 0) < 0) { + if (errno == EINTR) { + // interrupted system call - retry + continue; + } + } + break; + } + if (WIFEXITED(status)) { + val = WEXITSTATUS(status); + } + + return val; +} diff --git a/core/org.eclipse.cdt.core.native/native_src/unix/io.c b/core/org.eclipse.cdt.core.native/native_src/unix/io.c new file mode 100644 index 00000000000..94f03d63935 --- /dev/null +++ b/core/org.eclipse.cdt.core.native/native_src/unix/io.c @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2002, 2006 QNX Software Systems 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: + * QNX Software Systems - initial API and implementation + * Wind River Systems, Inc. + *******************************************************************************/ +#include <jni.h> +#include <stdio.h> +#include <SpawnerInputStream.h> +#include <SpawnerOutputStream.h> +#include <unistd.h> + +/* Header for class _org_eclipse_cdt_utils_spawner_SpawnerInputStream */ +/* Header for class _org_eclipse_cdt_utils_spawner_SpawnerOutputStream */ + +/* + * Class: org_eclipse_cdt_utils_spawner_SpawnerInputStream + * Method: read0 + * Signature: (I)I + */ +JNIEXPORT jint JNICALL +Java_org_eclipse_cdt_utils_spawner_SpawnerInputStream_read0(JNIEnv * env, + jobject jobj, + jint jfd, + jbyteArray buf, + jint buf_len) +{ + int fd; + int status; + jbyte *data; + int data_len; + + data = (*env)->GetByteArrayElements(env, buf, 0); + data_len = buf_len; + fd = jfd; + + status = read( fd, data, data_len ); + (*env)->ReleaseByteArrayElements(env, buf, data, 0); + + if (status == 0) { + /* EOF. */ + status = -1; + } else if (status == -1) { + /* Error, toss an exception */ + jclass exception = (*env)->FindClass(env, "java/io/IOException"); + if (exception == NULL) { + /* Give up. */ + return -1; + } + (*env)->ThrowNew(env, exception, "read error"); + } + + return status; +} + + +/* + * Class: org_eclipse_cdt_utils_spawner_SpawnerInputStream + * Method: close0 + * Signature: (I)I + */ +JNIEXPORT jint JNICALL +Java_org_eclipse_cdt_utils_spawner_SpawnerInputStream_close0(JNIEnv * env, + jobject jobj, + jint fd) +{ + return close(fd); +} + +/* + * Class: org_eclipse_cdt_utils_spawner_SpawnerOutputStream + * Method: write0 + * Signature: (II)I + */ +JNIEXPORT jint JNICALL +Java_org_eclipse_cdt_utils_spawner_SpawnerOutputStream_write0(JNIEnv * env, + jobject jobj, + jint jfd, + jbyteArray buf, + jint buf_len) +{ + int status; + int fd; + jbyte *data; + int data_len; + + data = (*env)->GetByteArrayElements(env, buf, 0); + data_len = buf_len; + fd = jfd; + + status = write(fd, data, data_len); + (*env)->ReleaseByteArrayElements(env, buf, data, 0); + + return status; +} + + +/* + * Class: org_eclipse_cdt_utils_spawner_SpawnerOutputStream + * Method: close0 + * Signature: (I)I + */ +JNIEXPORT jint JNICALL +Java_org_eclipse_cdt_utils_spawner_SpawnerOutputStream_close0(JNIEnv * env, + jobject jobj, + jint fd) +{ + return close(fd); +} diff --git a/core/org.eclipse.cdt.core.native/native_src/unix/openpty.c b/core/org.eclipse.cdt.core.native/native_src/unix/openpty.c new file mode 100644 index 00000000000..2ca02e2278c --- /dev/null +++ b/core/org.eclipse.cdt.core.native/native_src/unix/openpty.c @@ -0,0 +1,137 @@ +/******************************************************************************* + * Copyright (c) 2002, 2010 QNX Software Systems 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: + * QNX Software Systems - initial API and implementation + * Wind River Systems, Inc. + * Mikhail Zabaluev (Nokia) - bug 82744 + * Corey Ashford (IBM) - bug 272370, bug 272372 + *******************************************************************************/ + +/* _XOPEN_SOURCE is needed to bring in the header for ptsname */ +#define _XOPEN_SOURCE +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <termios.h> +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <grp.h> + +#include <stdlib.h> + +/** + * This is taken from R. W. Stevens book. + * Alain Magloire. + */ + +int ptym_open (char *pts_name); +int ptys_open (int fdm, const char * pts_name); +void set_noecho(int fd); + +int +openpty(int *amaster, int *aslave, char *name, struct termios *termp, struct winsize *winp) +{ + char line[20]; + line[0]=0; + *amaster = ptym_open(line); + if (*amaster < 0) + return -1; + *aslave = ptys_open(*amaster, line); + if (*aslave < 0) { + close(*amaster); + return -1; + } + + if (name) + strcpy(name, line); +#ifndef TCSAFLUSH +#define TCSAFLUSH TCSETAF +#endif + if (termp) + (void) tcsetattr(*aslave, TCSAFLUSH, termp); +#ifdef TIOCSWINSZ + if (winp) + (void) ioctl(*aslave, TIOCSWINSZ, (char *)winp); +#endif + return 0; +} + +void +set_noecho(int fd) +{ + struct termios stermios; + if (tcgetattr(fd, &stermios) < 0) { + return ; + } + + /* turn off echo */ + stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); + /* Turn off the NL to CR/NL mapping ou output. */ + /*stermios.c_oflag &= ~(ONLCR);*/ + + stermios.c_iflag |= (IGNCR); + + tcsetattr(fd, TCSANOW, &stermios); +} + +int +ptym_open(char * pts_name) +{ + int fdm; + char *ptr; + + strcpy(pts_name, "/dev/ptmx"); +#ifdef __APPLE__ + fdm = posix_openpt(O_RDWR|O_NOCTTY); +#else + fdm = getpt(); +#endif + if (fdm < 0) + return -1; + if (grantpt(fdm) < 0) { /* grant access to slave */ + close(fdm); + return -2; + } + if (unlockpt(fdm) < 0) { /* clear slave's lock flag */ + close(fdm); + return -3; + } + ptr = ptsname(fdm); + if (ptr == NULL) { /* get slave's name */ + close (fdm); + return -4; + } + strcpy(pts_name, ptr); /* return name of slave */ + return fdm; /* return fd of master */ +} + +int +ptys_open(int fdm, const char * pts_name) +{ + int fds; + /* following should allocate controlling terminal */ + fds = open(pts_name, O_RDWR); + if (fds < 0) { + close(fdm); + return -5; + } + +#if defined(TIOCSCTTY) + /* TIOCSCTTY is the BSD way to acquire a controlling terminal. */ + if (ioctl(fds, TIOCSCTTY, (char *)0) < 0) { + // ignore error: this is expected in console-mode + } +#endif + return fds; +} diff --git a/core/org.eclipse.cdt.core.native/native_src/unix/openpty.h b/core/org.eclipse.cdt.core.native/native_src/unix/openpty.h new file mode 100644 index 00000000000..234a306415d --- /dev/null +++ b/core/org.eclipse.cdt.core.native/native_src/unix/openpty.h @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2002, 2010 QNX Software Systems 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: + * QNX Software Systems - initial API and implementation + * Wind River Systems, Inc. + * Mikhail Zabaluev (Nokia) - bug 82744 + *******************************************************************************/ +#ifndef _OPENPTY_H +#define _OPENPTY_H +int ptym_open (char *pts_name); +int ptys_open (int fdm, const char * pts_name); +void set_noecho(int fd); +#endif diff --git a/core/org.eclipse.cdt.core.native/native_src/unix/pfind.c b/core/org.eclipse.cdt.core.native/native_src/unix/pfind.c new file mode 100644 index 00000000000..dd7abddf2f6 --- /dev/null +++ b/core/org.eclipse.cdt.core.native/native_src/unix/pfind.c @@ -0,0 +1,112 @@ +/******************************************************************************* + * Copyright (c) 2002, 2010 QNX Software Systems 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: + * QNX Software Systems - initial API and implementation + * Wind River Systems, Inc. + * Mikhail Sennikovsky - bug 145737 + * Everton Rufino Constantino (IBM) - bug 237611 + *******************************************************************************/ +/* + * pfind.c - Search for a binary in $PATH. + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> + +#ifndef PATH_MAX +#define PATH_MAX 1024 +#endif + +#define PATH_DEF "PATH=" +const int path_def_len = 5; /* strlen(PATH_DEF); */ + +char * path_val(char * const envp[]) +{ + int i; + if (envp == NULL || envp[0] == NULL) + return getenv("PATH" ); + + for(i = 0; envp[i] != NULL; i++){ + char* p = envp[i]; + if(!strncmp(PATH_DEF, p, path_def_len)){ + return p + path_def_len; + } + } + + return NULL; +} + +char * pfind(const char *name, char * const envp[]) +{ + char *tok; + char *sp; + char *path; + char fullpath[PATH_MAX+1]; + + /* Sanity check. */ + if (name == NULL) { + fprintf(stderr, "pfind(): Null argument.\n"); + return NULL; + } + + /* For absolute name or name with a path, check if it is an executable. */ + if (name[0] == '/' || name[0] == '.') { + if (access(name, X_OK) == 0) { + return strdup(name); + } + return NULL; + } + + /* Search in the PATH environment. */ + path = path_val( envp ); + + if (path == NULL || strlen(path) <= 0) { + fprintf(stderr, "Unable to get $PATH.\n"); + return NULL; + } + + /* The value return by getenv() is readonly */ + path = strdup(path); + + tok = strtok_r(path, ":", &sp); + while (tok != NULL) { + snprintf(fullpath, sizeof(fullpath) - 1, "%s/%s", tok, name); + + if (access(fullpath, X_OK) == 0) { + free(path); + return strdup(fullpath); + } + + tok = strtok_r( NULL, ":", &sp ); + } + + free(path); + return NULL; +} + +#ifdef BUILD_WITH_MAIN +int main(int argc, char **argv) +{ + int i; + char *fullpath; + + for (i=1; i<argc; i++) { + fullpath = pfind(argv[i], NULL); + if (fullpath == NULL) + printf("Unable to find %s in $PATH.\n", argv[i]); + else + printf("Found %s @ %s.\n", argv[i], fullpath); + } +} +#endif diff --git a/core/org.eclipse.cdt.core.native/native_src/unix/pty.c b/core/org.eclipse.cdt.core.native/native_src/unix/pty.c new file mode 100644 index 00000000000..edb87661eb5 --- /dev/null +++ b/core/org.eclipse.cdt.core.native/native_src/unix/pty.c @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2002, 2010 QNX Software Systems 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: + * QNX Software Systems - initial API and implementation + * Wind River Systems, Inc. + *******************************************************************************/ +#include <sys/ioctl.h> +#include "PTY.h" +#include "openpty.h" + +/* + * Class: org_eclipse_cdt_utils_pty_PTY + * Method: forkpty + * Signature: ()I + */ +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; + int master = -1; + char line[1024]; /* FIXME: Should be enough */ + jclass cls; + + line[0] = '\0'; + + master = ptym_open(line); + if (master >= 0) { + // turn off echo + if (console) { + set_noecho(master); + } + + /* Get a reference to the obj's class */ + cls = (*env)->GetObjectClass(env, jobj); + + /* Set the master fd. */ + fid = (*env)->GetFieldID(env, cls, "master", "I"); + if (fid == NULL) { + return NULL; + } + (*env)->SetIntField(env, jobj, fid, (jint)master); + + /* Create a new String for the slave. */ + jstr = (*env)->NewStringUTF(env, line); + } + return jstr; +} + +JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTY_change_1window_1size + (JNIEnv *env, jobject jobj, jint fdm, jint width, jint height) +{ +#ifdef TIOCSWINSZ + struct winsize win; + + win.ws_col = width; + win.ws_row = height; + win.ws_xpixel = 0; + win.ws_ypixel = 0; + + return ioctl(fdm, TIOCSWINSZ, &win); +#else +#error no TIOCSWINSZ + return 0; +#endif +} + diff --git a/core/org.eclipse.cdt.core.native/native_src/unix/ptyio.c b/core/org.eclipse.cdt.core.native/native_src/unix/ptyio.c new file mode 100644 index 00000000000..e022db11f99 --- /dev/null +++ b/core/org.eclipse.cdt.core.native/native_src/unix/ptyio.c @@ -0,0 +1,120 @@ +/******************************************************************************* + * Copyright (c) 2002 - 2005 QNX Software Systems 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: + * QNX Software Systems - initial API and implementation + *******************************************************************************/ +#include <jni.h> +#include <stdio.h> +#include <PTYInputStream.h> +#include <PTYOutputStream.h> +#include <unistd.h> + +/* Header for class _org_eclipse_cdt_utils_pty_PTYInputStream */ +/* Header for class _org_eclipse_cdt_utils_pty_PTYOutputStream */ + +/* + * Class: org_eclipse_cdt_utils_pty_PTYInputStream + * Method: read0 + * Signature: (I)I + */ +JNIEXPORT jint JNICALL +Java_org_eclipse_cdt_utils_pty_PTYInputStream_read0(JNIEnv * env, + jobject jobj, + jint jfd, + jbyteArray buf, + jint buf_len) +{ + int fd; + int status; + jbyte *data; + int data_len; + + data = (*env)->GetByteArrayElements(env, buf, 0); + data_len = buf_len; + fd = jfd; + + status = read( fd, data, data_len ); + (*env)->ReleaseByteArrayElements(env, buf, data, 0); + + if (status == 0) { + /* EOF. */ + status = -1; + } else if (status == -1) { + /* Error, toss an exception */ + /* Ignore the error for now, the debugger will attempt + * to close this multiple time. */ +#if 0 + jclass exception = (*env)->FindClass(env, "java/io/IOException"); + if (exception == NULL) { + /* Give up. */ + return -1; + } + (*env)->ThrowNew(env, exception, "read error"); +#endif + } + + return status; +} + + +/* + * 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 fd) +{ + return close(fd); +} + +/* + * Class: org_eclipse_cdt_utils_pty_PTYOutputStream + * Method: write0 + * Signature: (II)I + */ +JNIEXPORT jint JNICALL +Java_org_eclipse_cdt_utils_pty_PTYOutputStream_write0(JNIEnv * env, + jobject jobj, + jint jfd, + jbyteArray buf, + jint buf_len) +{ + int status; + int fd; + jbyte *data; + int data_len; + + data = (*env)->GetByteArrayElements(env, buf, 0); + data_len = buf_len; + fd = jfd; + + status = write(fd, data, data_len); + (*env)->ReleaseByteArrayElements(env, buf, data, 0); + + return status; +} + + +/* + * 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 fd) +{ + return close(fd); +} diff --git a/core/org.eclipse.cdt.core.native/native_src/unix/spawner.c b/core/org.eclipse.cdt.core.native/native_src/unix/spawner.c new file mode 100644 index 00000000000..623b1382cf8 --- /dev/null +++ b/core/org.eclipse.cdt.core.native/native_src/unix/spawner.c @@ -0,0 +1,305 @@ +/******************************************************************************* + * Copyright (c) 2002, 2010 QNX Software Systems 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: + * QNX Software Systems - initial API and implementation + * Wind River Systems, Inc. + * Mikhail Zabaluev (Nokia) - bug 82744 + *******************************************************************************/ +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <signal.h> +#include <string.h> +#include <jni.h> + +#include "exec0.h" +#include <Spawner.h> + + +#define DEBUGIT 0 + + +/* + * Header for class org_eclipse_cdt_utils_spawner_Spawner + */ + + +#if DEBUGIT +static void print_array(char **c_array) +{ + if (c_array) { + char **p = c_array; + for (; *p; p++) { + if (*p) { + fprintf(stderr, " %s", *p); + } + } + } else { + fprintf(stderr, "null"); + } + fprintf(stderr, "\n"); +} +#endif + + +static char **alloc_c_array(JNIEnv * env, jobjectArray j_array) +{ + int i; + jint c_array_size = (*env)->GetArrayLength(env, j_array); + char **c_array = calloc(c_array_size + 1, sizeof(*c_array)); + + if (c_array == NULL) + return NULL; + + for (i = 0; i < c_array_size; i++) { + jstring j_str = + (jstring) (*env)->GetObjectArrayElement(env, j_array, i); + const char *c_str = (*env)->GetStringUTFChars(env, j_str, NULL); + c_array[i] = (char *) strdup(c_str); + (*env)->ReleaseStringUTFChars(env, j_str, c_str); + (*env)->DeleteLocalRef(env, j_str); + } + + return c_array; +} + + +static void free_c_array(char **c_array) +{ + if (c_array) { + char **p = c_array; + for (; *p; p++) { + if (*p) { + free(*p); + } + } + free(c_array); + } +} + + +/* + * Class: org_eclipse_cdt_utils_spawner_Spawner + * Method: exec2 + * Signature: ([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[ILorg/eclipse/cdt/utils/pty/PTY;)I + */ +JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec2 + (JNIEnv *env, jobject jobj, jobjectArray jcmd, jobjectArray jenv, jstring jdir, jintArray jchannels, + jstring jslaveName, jint masterFD, jboolean console) +{ + jint *channels = (*env)->GetIntArrayElements(env, jchannels, 0); + const char *dirpath = (*env)->GetStringUTFChars(env, jdir, NULL); + const char *pts_name = (*env)->GetStringUTFChars(env, jslaveName, NULL); + char **cmd = NULL; + char **envp = NULL; + int fd[3]; + pid_t pid = -1; + + if (channels == NULL) + goto bail_out; + + cmd = alloc_c_array(env, jcmd); + if (cmd == NULL) + goto bail_out; + + envp = alloc_c_array(env, jenv); + if (envp == NULL) + goto bail_out; + +#if DEBUGIT + fprintf(stderr, "command:"); + print_array(cmd); + fprintf(stderr, "Envp:"); + print_array(envp); + fprintf(stderr, "dirpath: %s\n", dirpath); + fprintf(stderr, "pts_name: %s\n", pts_name); +#endif + + pid = exec_pty(cmd[0], cmd, envp, dirpath, fd, pts_name, masterFD, console); + if (pid < 0) + goto bail_out; + + channels[0] = fd[0]; + channels[1] = fd[1]; + channels[2] = fd[2]; + + bail_out: + (*env)->ReleaseIntArrayElements(env, jchannels, channels, 0); + (*env)->ReleaseStringUTFChars(env, jdir, dirpath); + (*env)->ReleaseStringUTFChars(env, jslaveName, pts_name); + if (cmd) + free_c_array(cmd); + if (envp) + free_c_array(envp); + return pid; +} + + +JNIEXPORT jint JNICALL +Java_org_eclipse_cdt_utils_spawner_Spawner_exec1(JNIEnv * env, jobject jobj, + jobjectArray jcmd, + jobjectArray jenv, + jstring jdir) +{ + const char *dirpath = (*env)->GetStringUTFChars(env, jdir, NULL); + char **cmd = NULL; + char **envp = NULL; + pid_t pid = -1; + + cmd = alloc_c_array(env, jcmd); + if (cmd == NULL) + goto bail_out; + + envp = alloc_c_array(env, jenv); + if (envp == NULL) + goto bail_out; + +#if DEBUGIT + fprintf(stderr, "command:"); + print_array(cmd); + fprintf(stderr, "Envp:"); + print_array(envp); + fprintf(stderr, "dirpath: %s\n", dirpath); +#endif + + pid = exec0(cmd[0], cmd, envp, dirpath, NULL); + if (pid < 0) + goto bail_out; + + bail_out: + (*env)->ReleaseStringUTFChars(env, jdir, dirpath); + if (cmd) + free_c_array(cmd); + if (envp) + free_c_array(envp); + return pid; +} + +/* + * Class: org_eclipse_cdt_utils_spawner_Spawner + * Method: exec0 + * Signature: ([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[I)I + */ +JNIEXPORT jint JNICALL +Java_org_eclipse_cdt_utils_spawner_Spawner_exec0(JNIEnv * env, jobject jobj, + jobjectArray jcmd, + jobjectArray jenv, + jstring jdir, + jintArray jchannels) +{ + jint *channels = (*env)->GetIntArrayElements(env, jchannels, 0); + const char *dirpath = (*env)->GetStringUTFChars(env, jdir, NULL); + char **cmd = NULL; + char **envp = NULL; + int fd[3]; + pid_t pid = -1; + + if (channels == NULL) + goto bail_out; + + cmd = alloc_c_array(env, jcmd); + if (cmd == NULL) + goto bail_out; + + envp = alloc_c_array(env, jenv); + if (envp == NULL) + goto bail_out; + +#if DEBUGIT + fprintf(stderr, "command:"); + print_array(cmd); + fprintf(stderr, "Envp:"); + print_array(envp); + fprintf(stderr, "dirpath: %s\n", dirpath); +#endif + + pid = exec0(cmd[0], cmd, envp, dirpath, fd); + if (pid < 0) + goto bail_out; + + channels[0] = fd[0]; + channels[1] = fd[1]; + channels[2] = fd[2]; + + bail_out: + (*env)->ReleaseIntArrayElements(env, jchannels, channels, 0); + (*env)->ReleaseStringUTFChars(env, jdir, dirpath); + if (cmd) + free_c_array(cmd); + if (envp) + free_c_array(envp); + return pid; +} + +/* + * Class: org_eclipse_cdt_utils_spawner_Spawner + * Method: raise + * Signature: (II)I + */ +JNIEXPORT jint JNICALL +Java_org_eclipse_cdt_utils_spawner_Spawner_raise(JNIEnv * env, jobject jobj, + jint pid, jint sig) +{ + int status = -1; + + switch (sig) { + case 0: /* NOOP */ + status = killpg(pid, 0); + if(status == -1) { + status = kill(pid, 0); + } + break; + + case 2: /* INTERRUPT */ + status = killpg(pid, SIGINT); + if(status == -1) { + status = kill(pid, SIGINT); + } + break; + + case 9: /* KILL */ + status = killpg(pid, SIGKILL); + if(status == -1) { + status = kill(pid, SIGKILL); + } + break; + + case 15: /* TERM */ + status = killpg(pid, SIGTERM); + if(status == -1) { + status = kill(pid, SIGTERM); + } + break; + + default: + status = killpg(pid, sig); /* WHAT ?? */ + if(status == -1) { + status = kill(pid, sig); /* WHAT ?? */ + } + break; + } + + return status; +} + + + +/* + * Class: org_eclipse_cdt_utils_spawner_Spawner + * Method: waitFor + * Signature: (I)I + */ +JNIEXPORT jint JNICALL +Java_org_eclipse_cdt_utils_spawner_Spawner_waitFor(JNIEnv * env, + jobject jobj, jint pid) +{ + return wait0(pid); +} diff --git a/core/org.eclipse.cdt.core.native/native_src/win/Win32ProcessEx.c b/core/org.eclipse.cdt.core.native/native_src/win/Win32ProcessEx.c new file mode 100644 index 00000000000..7b6bcc13699 --- /dev/null +++ b/core/org.eclipse.cdt.core.native/native_src/win/Win32ProcessEx.c @@ -0,0 +1,1018 @@ +/******************************************************************************* + * Copyright (c) 2002, 2015 QNX Software Systems 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: + * QNX Software Systems - initial API and implementation + * Wind River Systems, Inc. + * + * Win32ProcessEx.c + * + * This is a JNI implementation of spawner + *******************************************************************************/ + +#include <string.h> +#include <stdlib.h> +#include <process.h> +#include <tchar.h> +#include <windows.h> +#include <jni.h> + +#include "Spawner.h" + +#define PIPE_SIZE 512 // Size of pipe buffer +#define MAX_CMD_SIZE 2049 // Initial size of command line +#define MAX_ENV_SIZE 4096 // Initial size of environment block +#define PIPE_NAME_LENGTH 100 // Size of pipe name buffer +#define PIPE_TIMEOUT 10000 // Default time-out value, in milliseconds. + +#define MAX_PROCS (100) // Maximum number of simultaneously running processes + + +// Process description block. Should be created for each launched process +typedef struct _procInfo { + int pid; // Process ID + int uid; // quasi-unique process ID; we have to create it to avoid duplicated pid + // (actually this impossible from OS point of view but it is still possible + // a clash of new created and already finished process with one and the same PID. + // 4 events connected to this process (see starter) + HANDLE eventBreak; // signaled when Spawner.interrupt() is called; mildest of the terminate requests (SIGINT signal in UNIX world) + HANDLE eventWait; + HANDLE eventTerminate; // signaled when Spawner.terminate() is called; more forceful terminate request (SIGTERM signal in UNIX world) + HANDLE eventKill; // signaled when Spawner.kill() is called; most forceful terminate request (SIGKILL signal in UNIX world) + HANDLE eventCtrlc; // signaled when Spawner.interruptCTRLC() is called; like interrupt() but sends CTRL-C in all cases, even when inferior is a Cygwin program +} procInfo_t, * pProcInfo_t; + +static int procCounter = 0; // Number of running processes + + +// This is a VM helper +void ThrowByName(JNIEnv *env, const char *name, const char *msg); + +// Creates _procInfo block for every launched process +pProcInfo_t createProcInfo(); + +// Find process description for this pid +pProcInfo_t findProcInfo(int pid); + +// We launch separate thread for each project to trap it termination +void _cdecl waitProcTermination(void* pv) ; + +// This is a helper function to prevent losing of quotation marks +static int copyTo(wchar_t * target, const wchar_t * source, int cpyLenght, int availSpace); + +// Use this function to clean project descriptor and return it to the pool of available blocks. +static void cleanUpProcBlock(pProcInfo_t pCurProcInfo); + + +// Signal codes +typedef enum { + SIG_NOOP, + SIG_HUP, + SIG_INT, + SIG_KILL = 9, + SIG_TERM = 15, + CTRLC = 1000 // special, Windows only. Sends CTRL-C in all cases, even when inferior is a Cygwin program +} signals; + +extern CRITICAL_SECTION cs; + + +extern wchar_t path[MAX_PATH]; // Directory where spawner.dll is located + +static HMODULE hVM = NULL; // VM handler + + +static pProcInfo_t pInfo = NULL; + +static int nCounter = 0; // We use it to build unique synchronization object names + +///////////////////////////////////////////////////////////////////////////////////// +// Launcher; launchess process and traps its termination +// Arguments: (see Spawner.java) +// [in] cmdarray - array of command line elements +// [in] envp - array of environment variables +// [in] dir - working directory +// [out] channels - streams handlers +///////////////////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus +extern "C" +#endif +JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec2 + (JNIEnv * env, jobject process, jobjectArray cmdarray, jobjectArray envp, jstring dir, jintArray channels, jstring slaveName, jint fdm, jboolean console) +{ + return -1; +} + +void ensureSize(wchar_t** ptr, int* psize, int requiredLength) +{ + int size= *psize; + if (requiredLength > size) { + size= 2*size; + if (size < requiredLength) { + size= requiredLength; + } + *ptr= (wchar_t *)realloc(*ptr, size * sizeof(wchar_t)); + if (NULL == *ptr) { + *psize= 0; + } + else { + *psize= size; + } + } +} + +#ifdef __cplusplus +extern "C" +#endif +JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec0 + (JNIEnv * env, jobject process, jobjectArray cmdarray, jobjectArray envp, jstring dir, jintArray channels) +{ + HANDLE stdHandles[3]; + PROCESS_INFORMATION pi = {0}, *piCopy; + STARTUPINFOW si; + DWORD flags = 0; + const wchar_t * cwd = NULL; + LPVOID envBlk = NULL; + int ret = 0; + int nCmdLineLength= 0; + wchar_t * szCmdLine= 0; + int nBlkSize = MAX_ENV_SIZE; + wchar_t * szEnvBlock = NULL; + jsize nCmdTokens = 0; + jsize nEnvVars = 0; + int i; + DWORD pid = GetCurrentProcessId(); + int nPos; + pProcInfo_t pCurProcInfo; + + // This needs to be big enough to contain the name of the event used when calling CreateEventW bellow. + // It is made of a prefix (7 characters max) plus the value of a pointer that gets output in characters. + // This will be bigger in the case of 64 bit. + static const int MAX_EVENT_NAME_LENGTH = 50; + wchar_t eventBreakName[MAX_EVENT_NAME_LENGTH]; + wchar_t eventWaitName[MAX_EVENT_NAME_LENGTH]; + wchar_t eventTerminateName[MAX_EVENT_NAME_LENGTH]; + wchar_t eventKillName[MAX_EVENT_NAME_LENGTH]; + wchar_t eventCtrlcName[MAX_EVENT_NAME_LENGTH]; +#ifdef DEBUG_MONITOR + wchar_t buffer[4000]; +#endif + int nLocalCounter; + wchar_t inPipeName[PIPE_NAME_LENGTH]; + wchar_t outPipeName[PIPE_NAME_LENGTH]; + wchar_t errPipeName[PIPE_NAME_LENGTH]; + + nCmdLineLength= MAX_CMD_SIZE; + szCmdLine= (wchar_t *)malloc(nCmdLineLength * sizeof(wchar_t)); + szCmdLine[0]= _T('\0'); + if((HIBYTE(LOWORD(GetVersion()))) & 0x80) + { + ThrowByName(env, "java/io/IOException", "Does not support Windows 3.1/95/98/Me"); + return 0; + } + + if (cmdarray == 0) + { + ThrowByName(env, "java/lang/NullPointerException", "No command line specified"); + return 0; + } + + ZeroMemory(stdHandles, sizeof(stdHandles)); + + // Create pipe names + EnterCriticalSection(&cs); + swprintf(inPipeName, sizeof(inPipeName)/sizeof(inPipeName[0]), L"\\\\.\\pipe\\stdin%08i%010i", pid, nCounter); + swprintf(outPipeName, sizeof(outPipeName)/sizeof(outPipeName[0]), L"\\\\.\\pipe\\stdout%08i%010i", pid, nCounter); + swprintf(errPipeName, sizeof(errPipeName)/sizeof(errPipeName[0]), L"\\\\.\\pipe\\stderr%08i%010i", pid, nCounter); + nLocalCounter = nCounter; + ++nCounter; + LeaveCriticalSection(&cs); + + if ((INVALID_HANDLE_VALUE == (stdHandles[0] = CreateNamedPipeW(inPipeName, PIPE_ACCESS_OUTBOUND, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, PIPE_SIZE, PIPE_SIZE, PIPE_TIMEOUT, NULL))) || + (INVALID_HANDLE_VALUE == (stdHandles[1] = CreateNamedPipeW(outPipeName, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, PIPE_SIZE, PIPE_SIZE, PIPE_TIMEOUT, NULL))) || + (INVALID_HANDLE_VALUE == (stdHandles[2] = CreateNamedPipeW(errPipeName, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, PIPE_SIZE, PIPE_SIZE, PIPE_TIMEOUT, NULL)))) { + CloseHandle(stdHandles[0]); + CloseHandle(stdHandles[1]); + CloseHandle(stdHandles[2]); + ThrowByName(env, "java/io/IOException", "CreatePipe"); + return 0; + } + +#ifdef DEBUG_MONITOR + swprintf(buffer, sizeof(buffer)/sizeof(buffer[0]), _T("Opened pipes: %s, %s, %s\n"), inPipeName, outPipeName, errPipeName); + OutputDebugStringW(buffer); +#endif + + + nCmdTokens = (*env)->GetArrayLength(env, cmdarray); + nEnvVars = (*env)->GetArrayLength(env, envp); + + pCurProcInfo = createProcInfo(); + + if(NULL == pCurProcInfo) + { + ThrowByName(env, "java/io/IOException", "Too many processes"); + return 0; + } + + // Construct starter's command line + swprintf(eventBreakName, sizeof(eventBreakName)/sizeof(eventBreakName[0]), L"SABreak%04x%08x", pid, nLocalCounter); + swprintf(eventWaitName, sizeof(eventWaitName)/sizeof(eventWaitName[0]), L"SAWait%04x%08x", pid, nLocalCounter); + swprintf(eventTerminateName, sizeof(eventTerminateName)/sizeof(eventTerminateName[0]), L"SATerm%04x%08x", pid, nLocalCounter); + swprintf(eventKillName, sizeof(eventKillName)/sizeof(eventKillName[0]), L"SAKill%04x%08x", pid, nLocalCounter); + swprintf(eventCtrlcName, sizeof(eventCtrlcName)/sizeof(eventCtrlcName[0]), L"SACtrlc%04x%08x", pid, nLocalCounter); + + pCurProcInfo->eventBreak = CreateEventW(NULL, FALSE, FALSE, eventBreakName); + if(NULL == pCurProcInfo->eventBreak || GetLastError() == ERROR_ALREADY_EXISTS) + { + ThrowByName(env, "java/io/IOException", "Cannot create event"); + return 0; + } + pCurProcInfo->eventWait = CreateEventW(NULL, TRUE, FALSE, eventWaitName); + pCurProcInfo->eventTerminate = CreateEventW(NULL, FALSE, FALSE, eventTerminateName); + pCurProcInfo->eventKill = CreateEventW(NULL, FALSE, FALSE, eventKillName); + pCurProcInfo->eventCtrlc = CreateEventW(NULL, FALSE, FALSE, eventCtrlcName); + + swprintf(szCmdLine, nCmdLineLength, L"\"%sstarter.exe\" %i %i %s %s %s %s %s ", path, pid, nLocalCounter, eventBreakName, eventWaitName, eventTerminateName, eventKillName, eventCtrlcName); + nPos = wcslen(szCmdLine); + + // Prepare command line + for(i = 0; i < nCmdTokens; ++i) + { + jstring item = (jstring)(*env)->GetObjectArrayElement(env, cmdarray, i); + jsize len = (*env)->GetStringLength(env, item); + int nCpyLen; + const wchar_t * str = (const wchar_t *)(*env)->GetStringChars(env, item, 0); + if(NULL != str) + { + int requiredSize= nPos+len+2; + if (requiredSize > 32*1024) { + ThrowByName(env, "java/io/IOException", "Command line too long"); + return 0; + } + ensureSize(&szCmdLine, &nCmdLineLength, requiredSize); + if (NULL == szCmdLine) { + ThrowByName(env, "java/io/IOException", "Not enough memory"); + return 0; + } + + if(0 > (nCpyLen = copyTo(szCmdLine + nPos, str, len, nCmdLineLength - nPos))) + { + ThrowByName(env, "java/io/IOException", "Command line too long"); + return 0; + } + nPos += nCpyLen; + szCmdLine[nPos] = _T(' '); + ++nPos; + (*env)->ReleaseStringChars(env, item, (const jchar *)str); + } + } + szCmdLine[nPos] = _T('\0'); + +#ifdef DEBUG_MONITOR + swprintf(buffer, sizeof(buffer)/sizeof(buffer[0]), _T("There are %i environment variables \n"), nEnvVars); + OutputDebugStringW(buffer); +#endif + // Prepare environment block + if (nEnvVars > 0) + { + nPos = 0; + szEnvBlock = (wchar_t *)malloc(nBlkSize * sizeof(wchar_t)); + for(i = 0; i < nEnvVars; ++i) + { + jstring item = (jstring)(*env)->GetObjectArrayElement(env, envp, i); + jsize len = (*env)->GetStringLength(env, item); + const wchar_t * str = (const wchar_t *)(*env)->GetStringChars(env, item, 0); + if(NULL != str) + { + while((nBlkSize - nPos) <= (len + 2)) // +2 for two '\0' + { + nBlkSize += MAX_ENV_SIZE; + szEnvBlock = (wchar_t *)realloc(szEnvBlock, nBlkSize * sizeof(wchar_t)); + if(NULL == szEnvBlock) + { + ThrowByName(env, "java/io/IOException", "Not enough memory"); + return 0; + } +#ifdef DEBUG_MONITOR + swprintf(buffer, sizeof(buffer)/sizeof(buffer[0]), _T("Realloc environment block; new length is %i \n"), nBlkSize); + OutputDebugStringW(buffer); +#endif + + } +#ifdef DEBUG_MONITOR + swprintf(buffer, sizeof(buffer)/sizeof(buffer[0]), _T("%s\n"), str); + OutputDebugStringW(buffer); +#endif + wcsncpy(szEnvBlock + nPos, str, len); + nPos += len; + szEnvBlock[nPos] = _T('\0'); + ++nPos; + (*env)->ReleaseStringChars(env, item, (const jchar *)str); + } + } + szEnvBlock[nPos] = _T('\0'); + } + + + + if (dir != 0) + { + const wchar_t * str = (const wchar_t *)(*env)->GetStringChars(env, dir, 0); + if(NULL != str) + { + cwd = wcsdup(str); + (*env)->ReleaseStringChars(env, dir, (const jchar *)str); + } + } + + + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags |= STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; // Processes in the Process Group are hidden + + + + SetHandleInformation(stdHandles[0], HANDLE_FLAG_INHERIT, FALSE); + SetHandleInformation(stdHandles[1], HANDLE_FLAG_INHERIT, FALSE); + SetHandleInformation(stdHandles[2], HANDLE_FLAG_INHERIT, FALSE); + + flags = CREATE_NEW_CONSOLE; + flags |= CREATE_NO_WINDOW; + flags |= CREATE_UNICODE_ENVIRONMENT; + +#ifdef DEBUG_MONITOR + OutputDebugStringW(szCmdLine); +#endif + // launches starter; we need it to create another console group to correctly process + // emulation of SYSint signal (Ctrl-C) + ret = CreateProcessW(0, /* executable name */ + szCmdLine, /* command line */ + 0, /* process security attribute */ + 0, /* thread security attribute */ + FALSE, /* inherits system handles */ + flags, /* normal attached process */ + szEnvBlock, /* environment block */ + cwd, /* change to the new current directory */ + &si, /* (in) startup information */ + &pi); /* (out) process information */ + + if(NULL != cwd) + free((void *)cwd); + + if(NULL != szEnvBlock) + free(szEnvBlock); + + if(NULL != szCmdLine) + free(szCmdLine); + + if (!ret) // Launching error + { + char * lpMsgBuf; + CloseHandle(stdHandles[0]); + CloseHandle(stdHandles[1]); + CloseHandle(stdHandles[2]); + FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (char *)&lpMsgBuf, + 0, + NULL + ); + ThrowByName(env, "java/io/IOException", lpMsgBuf); + // Free the buffer. + LocalFree( lpMsgBuf ); + cleanUpProcBlock(pCurProcInfo); + ret = -1; + } + else + { + int file_handles[3]; + HANDLE h[2]; + int what; + + EnterCriticalSection(&cs); + + pCurProcInfo -> pid = pi.dwProcessId; + h[0] = pCurProcInfo -> eventWait; + h[1] = pi.hProcess; + + what = WaitForMultipleObjects(2, h, FALSE, INFINITE); + if(what != WAIT_OBJECT_0) // CreateProcess failed + { +#ifdef DEBUG_MONITOR + swprintf(buffer, sizeof(buffer)/sizeof(buffer[0]), _T("Process %i failed\n"), pi.dwProcessId); + OutputDebugStringW(buffer); +#endif + cleanUpProcBlock(pCurProcInfo); + ThrowByName(env, "java/io/IOException", "Launching failed"); +#ifdef DEBUG_MONITOR + OutputDebugStringW(_T("Process failed\n")); +#endif + } + else + { + ret = (long)(pCurProcInfo -> uid); + + // Prepare stream handlers to return to java program + file_handles[0] = (int)stdHandles[0]; + file_handles[1] = (int)stdHandles[1]; + file_handles[2] = (int)stdHandles[2]; + (*env)->SetIntArrayRegion(env, channels, 0, 3, (jint *)file_handles); + + // do the cleanup so launch the according thread + // create a copy of the PROCESS_INFORMATION as this might get destroyed + piCopy = (PROCESS_INFORMATION *)malloc(sizeof(PROCESS_INFORMATION)); + memcpy(piCopy, &pi, sizeof(PROCESS_INFORMATION)); + _beginthread(waitProcTermination, 0, (void *)piCopy); + +#ifdef DEBUG_MONITOR + OutputDebugStringW(_T("Process started\n")); +#endif + } + LeaveCriticalSection(&cs); + + } + + CloseHandle(pi.hThread); + + return ret; + +} + + +///////////////////////////////////////////////////////////////////////////////////// +// Launcher; just launches process and don't care about it any more +// Arguments: (see Spawner.java) +// [in] cmdarray - array of command line elements +// [in] envp - array of environment variables +// [in] dir - working directory +///////////////////////////////////////////////////////////////////////////////////// +#ifdef __cplusplus +extern "C" +#endif +JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec1 + (JNIEnv * env, jobject process, jobjectArray cmdarray, jobjectArray envp, jstring dir) +{ + + SECURITY_ATTRIBUTES sa; + PROCESS_INFORMATION pi = {0}; + STARTUPINFOW si; + DWORD flags = 0; + wchar_t * cwd = NULL; + wchar_t * envBlk = NULL; + int ret = 0; + jsize nCmdTokens = 0; + jsize nEnvVars = 0; + int i; + int nPos; + int nCmdLineLength= 0; + wchar_t * szCmdLine= 0; + int nBlkSize = MAX_ENV_SIZE; + wchar_t * szEnvBlock = NULL; + + nCmdLineLength= MAX_CMD_SIZE; + szCmdLine= (wchar_t *)malloc(nCmdLineLength * sizeof(wchar_t)); + szCmdLine[0]= 0; + + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = 0; + sa.bInheritHandle = TRUE; + + + nCmdTokens = (*env)->GetArrayLength(env, cmdarray); + nEnvVars = (*env)->GetArrayLength(env, envp); + + nPos = 0; + + // Prepare command line + for(i = 0; i < nCmdTokens; ++i) + { + jstring item = (jstring)(*env)->GetObjectArrayElement(env, cmdarray, i); + jsize len = (*env)->GetStringLength(env, item); + int nCpyLen; + const wchar_t * str = (const wchar_t *)(*env)->GetStringChars(env, item, 0); + if(NULL != str) + { + int requiredSize= nPos+len+2; + if (requiredSize > 32*1024) { + ThrowByName(env, "java/io/IOException", "Command line too long"); + return 0; + } + ensureSize(&szCmdLine, &nCmdLineLength, requiredSize); + if (NULL == szCmdLine) { + ThrowByName(env, "java/io/IOException", "Not enough memory"); + return 0; + } + + if(0 > (nCpyLen = copyTo(szCmdLine + nPos, str, len, nCmdLineLength - nPos))) + { + ThrowByName(env, "java/io/Exception", "Command line too long"); + return 0; + } + nPos += nCpyLen; + szCmdLine[nPos] = _T(' '); + ++nPos; + (*env)->ReleaseStringChars(env, item, (const jchar *)str); + } + } + + szCmdLine[nPos] = _T('\0'); + + // Prepare environment block + if (nEnvVars > 0) + { + szEnvBlock = (wchar_t *)malloc(nBlkSize * sizeof(wchar_t)); + nPos = 0; + for(i = 0; i < nEnvVars; ++i) + { + jstring item = (jstring)(*env)->GetObjectArrayElement(env, envp, i); + jsize len = (*env)->GetStringLength(env, item); + const wchar_t * str = (const wchar_t *)(*env)->GetStringChars(env, item, 0); + if(NULL != str) + { + while((nBlkSize - nPos) <= (len + 2)) // +2 for two '\0' + { + nBlkSize += MAX_ENV_SIZE; + szEnvBlock = (wchar_t *)realloc(szEnvBlock, nBlkSize * sizeof(wchar_t)); + if(NULL == szEnvBlock) + { + ThrowByName(env, "java/io/Exception", "Not enough memory"); + return 0; + } + } + wcsncpy(szEnvBlock + nPos, str, len); + nPos += len; + szEnvBlock[nPos] = _T('\0'); + ++nPos; + (*env)->ReleaseStringChars(env, item, (const jchar *)str); + } + } + szEnvBlock[nPos] = _T('\0'); + envBlk = szEnvBlock; + } + + + + if (dir != 0) + { + const wchar_t * str = (const wchar_t *)(*env)->GetStringChars(env, dir, 0); + if(NULL != str) + { + cwd = wcsdup(str); + (*env)->ReleaseStringChars(env, dir, (const jchar *)str); + } + } + + + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + + + + + + flags = CREATE_NEW_CONSOLE; + flags |= CREATE_UNICODE_ENVIRONMENT; + ret = CreateProcessW(0, /* executable name */ + szCmdLine, /* command line */ + 0, /* process security attribute */ + 0, /* thread security attribute */ + TRUE, /* inherits system handles */ + flags, /* normal attached process */ + envBlk, /* environment block */ + cwd, /* change to the new current directory */ + &si, /* (in) startup information */ + &pi); /* (out) process information */ + + + + if(NULL != cwd) + free(cwd); + if(NULL != szEnvBlock) + free(szEnvBlock); + if(NULL != szCmdLine) + free(szCmdLine); + + if (!ret) // error + { + char * lpMsgBuf; + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (wchar_t *)&lpMsgBuf, + 0, + NULL + ); + ThrowByName(env, "java/io/IOException", lpMsgBuf); + // Free the buffer. + LocalFree( lpMsgBuf ); + ret = -1; + } + else + { + // Clean-up + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + ret = (long)pi.dwProcessId; //hProcess; + } + + + return ret; + +} + + +///////////////////////////////////////////////////////////////////////////////////// +// Emulation of the signal raising +// Arguments: (see Spawner.java) +// [in] uid - unique process ID +// [in] signal - signal to raise +///////////////////////////////////////////////////////////////////////////////////// +#ifdef __cplusplus +extern "C" +#endif +JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_raise + (JNIEnv * env, jobject process, jint uid, jint signal) +{ + jint ret = 0; + + HANDLE hProc; + pProcInfo_t pCurProcInfo = findProcInfo(uid); +#ifdef DEBUG_MONITOR + wchar_t buffer[100]; +#endif + + if(NULL == pCurProcInfo) { + if(SIG_INT == signal) { // Try another way + return interruptProcess(uid) ; + } + return -1; + } + +#ifdef DEBUG_MONITOR + swprintf(buffer, sizeof(buffer)/sizeof(buffer[0]), _T("Spawner received signal %i for process %i\n"), signal, pCurProcInfo -> pid); + OutputDebugStringW(buffer); +#endif + + hProc = OpenProcess(SYNCHRONIZE, 0, pCurProcInfo -> pid); + + if(NULL == hProc) + return -1; + + switch(signal) + { + case SIG_NOOP: + // Wait 0 msec -just check if the process has been still running + ret = ((WAIT_TIMEOUT == WaitForSingleObject(hProc, 0)) ? 0 : -1); + break; + case SIG_HUP: + // Temporary do nothing + ret = 0; + break; + case SIG_TERM: +#ifdef DEBUG_MONITOR + swprintf(buffer, sizeof(buffer)/sizeof(buffer[0]), _T("Spawner received TERM signal for process %i\n"), + pCurProcInfo -> pid); + OutputDebugStringW(buffer); +#endif + SetEvent(pCurProcInfo -> eventTerminate); +#ifdef DEBUG_MONITOR + OutputDebugStringW(_T("Spawner signaled TERM event\n")); +#endif + ret = 0; + break; + + case SIG_KILL: +#ifdef DEBUG_MONITOR + swprintf(buffer, sizeof(buffer)/sizeof(buffer[0]), _T("Spawner received KILL signal for process %i\n"), + pCurProcInfo -> pid); + OutputDebugStringW(buffer); +#endif + SetEvent(pCurProcInfo -> eventKill); +#ifdef DEBUG_MONITOR + OutputDebugStringW(_T("Spawner signaled KILL event\n")); +#endif + ret = 0; + break; + case SIG_INT: + ResetEvent(pCurProcInfo -> eventWait); + SetEvent(pCurProcInfo -> eventBreak); + ret = (WaitForSingleObject(pCurProcInfo -> eventWait, 100) == WAIT_OBJECT_0); + break; + case CTRLC: + ResetEvent(pCurProcInfo -> eventWait); + SetEvent(pCurProcInfo -> eventCtrlc); + ret = (WaitForSingleObject(pCurProcInfo -> eventWait, 100) == WAIT_OBJECT_0); + break; + default: + break; + } + + CloseHandle(hProc); + return ret; + + +} + + + +///////////////////////////////////////////////////////////////////////////////////// +// Wait for process termination +// Arguments: (see Spawner.java) +// [in] uid - unique process ID +///////////////////////////////////////////////////////////////////////////////////// +#ifdef __cplusplus +extern "C" +#endif +JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_waitFor + (JNIEnv * env, jobject process, jint uid) +{ + DWORD exit_code = -1; + int what=0; + HANDLE hProc; + pProcInfo_t pCurProcInfo = findProcInfo(uid); + + if(NULL == pCurProcInfo) + return -1; + + hProc = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, 0, pCurProcInfo -> pid); + + if(NULL == hProc) + return -1; + + what = WaitForSingleObject(hProc, INFINITE); + + + if (what == WAIT_OBJECT_0) + { + GetExitCodeProcess(hProc, &exit_code); + } + + + if(hProc) + CloseHandle(hProc); + + return exit_code; +} + + + + + +// Utilities + +///////////////////////////////////////////////////////////////////////////////////// +// Throws Java exception (will be trapped by VM). +// Arguments: +// [in] name - name of exception class +// [in] message to assign thi event +///////////////////////////////////////////////////////////////////////////////////// +void ThrowByName(JNIEnv *env, const char *name, const char *msg) +{ + jclass cls = (*env)->FindClass(env, name); + + if (cls != 0) /* Otherwise an exception has already been thrown */ + (*env)->ThrowNew(env, cls, msg); + + /* It's a good practice to clean up the local references. */ + (*env)->DeleteLocalRef(env, cls); +} + + + + +///////////////////////////////////////////////////////////////////////////////////// +// Create process description block. +// Arguments: no +// Return : pointer to the process descriptor +///////////////////////////////////////////////////////////////////////////////////// +pProcInfo_t createProcInfo() +{ + int i; + pProcInfo_t p = NULL; + + EnterCriticalSection(&cs); + + if(NULL == pInfo) + { + pInfo = (pProcInfo_t)malloc(sizeof(procInfo_t) * MAX_PROCS); + ZeroMemory(pInfo, sizeof(procInfo_t) * MAX_PROCS); + } + + for(i = 0; i < MAX_PROCS; ++i) + { + if(pInfo[i].pid == 0) + { + pInfo[i].pid = -1; + pInfo[i].uid = ++procCounter; + p = pInfo + i; + break; + } + } + + LeaveCriticalSection(&cs); + + return p; +} + +///////////////////////////////////////////////////////////////////////////////////// +// Using unique process ID finds process descriptor +// Arguments: no +// Return : pointer to the process descriptor +///////////////////////////////////////////////////////////////////////////////////// +pProcInfo_t findProcInfo(int uid) +{ + int i; + pProcInfo_t p = NULL; + if(NULL == pInfo) + return NULL; + + for(i = 0; i < MAX_PROCS; ++i) + { + if(pInfo[i].uid == uid) + { + p = pInfo + i; + break; + } + } + + return p; +} + +///////////////////////////////////////////////////////////////////////////////////// +// Cleans up vacant process descriptor +// Arguments: +// pCurProcInfo - pointer to descriptor to clean up +// Return : no +void cleanUpProcBlock(pProcInfo_t pCurProcInfo) +{ + if(0 != pCurProcInfo -> eventBreak) + { + CloseHandle(pCurProcInfo -> eventBreak); + pCurProcInfo -> eventBreak = 0; + } + if(0 != pCurProcInfo -> eventWait) + { + CloseHandle(pCurProcInfo -> eventWait); + pCurProcInfo -> eventWait = 0; + } + if(0 != pCurProcInfo -> eventTerminate) + { + CloseHandle(pCurProcInfo -> eventTerminate); + pCurProcInfo -> eventTerminate = 0; + } + + if(0 != pCurProcInfo -> eventKill) + { + CloseHandle(pCurProcInfo -> eventKill); + pCurProcInfo -> eventKill = 0; + } + + if(0 != pCurProcInfo -> eventCtrlc) + { + CloseHandle(pCurProcInfo -> eventCtrlc); + pCurProcInfo -> eventCtrlc = 0; + } + + pCurProcInfo -> pid = 0; +} + +///////////////////////////////////////////////////////////////////////////////////// +// Running in separate thread and waiting for the process termination +// Arguments: +// pv - pointer to PROCESS_INFORMATION struct +// Return : no +///////////////////////////////////////////////////////////////////////////////////// +void _cdecl waitProcTermination(void* pv) +{ + PROCESS_INFORMATION *pi = (PROCESS_INFORMATION *)pv; + int i; +#ifdef DEBUG_MONITOR + wchar_t buffer[1000]; +#endif + + // wait for process termination + WaitForSingleObject(pi->hProcess, INFINITE); + + for(i = 0; i < MAX_PROCS; ++i) + { + if(pInfo[i].pid == pi->dwProcessId) + { + cleanUpProcBlock(pInfo + i); +#ifdef DEBUG_MONITOR + swprintf(buffer, sizeof(buffer)/sizeof(buffer[0]), _T("waitProcTermination: set PID %i to 0\n"), + pi->dwProcessId); + OutputDebugStringW(buffer); +#endif + } + } + CloseHandle(pi->hProcess); + + free(pi); +} + +///////////////////////////////////////////////////////////////////////////////////// +// Use this utility program to process correctly quotation marks in the command line +// Arguments: +// target - string to copy to +// source - string to copy from +// cpyLength - copy length +// availSpace - size of the target buffer +// Return :number of bytes used in target, or -1 in case of error +///////////////////////////////////////////////////////////////////////////////////// +int copyTo(wchar_t * target, const wchar_t * source, int cpyLength, int availSpace) +{ + BOOL bSlash = FALSE; + int i = 0, j = 0; + int totCpyLength = cpyLength; + +#define QUOTATION_DO 0 +#define QUOTATION_DONE 1 +#define QUOTATION_NONE 2 + + int nQuotationMode = 0; + + + + if(availSpace <= cpyLength) // = to reserve space for final '\0' + return -1; + + if((_T('\"') == *source) && (_T('\"') == *(source + cpyLength - 1))) + { + nQuotationMode = QUOTATION_DONE; + } + else + if(wcschr(source, _T(' ')) == NULL) + { + // No reason to quote term because it doesn't have embedded spaces + nQuotationMode = QUOTATION_NONE; + } + else + { + // Needs to be quoted + nQuotationMode = QUOTATION_DO; + *target = _T('\"'); + ++j; + } + + + for(; i < cpyLength; ++i, ++j) + { + if(source[i] == _T('\\')) + bSlash = TRUE; + else + { + // Don't escape embracing quotation marks + if((source[i] == _T('\"')) && !((nQuotationMode == QUOTATION_DONE) && ((i == 0) || (i == (cpyLength - 1))) ) ) + { + if(!bSlash) // If still not escaped + { + if(j == availSpace) + return -1; + target[j] = _T('\\'); + ++j; + } + } + bSlash = FALSE; + } + + if(j == availSpace) + return -1; + target[j] = source[i]; + } + + if(nQuotationMode == QUOTATION_DO) + { + if(j == availSpace) + return -1; + target[j] = _T('\"'); + ++j; + } + + return j; +} diff --git a/core/org.eclipse.cdt.core.native/native_src/win/iostream.c b/core/org.eclipse.cdt.core.native/native_src/win/iostream.c new file mode 100644 index 00000000000..7fcfaf90d18 --- /dev/null +++ b/core/org.eclipse.cdt.core.native/native_src/win/iostream.c @@ -0,0 +1,279 @@ +/******************************************************************************* + * Copyright (c) 2002, 2009 QNX Software Systems 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: + * QNX Software Systems - initial API and implementation + * + * raise.c + * + * This is a part of JNI implementation of spawner + * Includes implementation of JNI methods (see Spawner.java) + *******************************************************************************/ +#include <string.h> +#include <stdlib.h> +#include <jni.h> +#include <windows.h> + +#include "Spawner.h" + +//#define READ_REPORT + +void ThrowByName(JNIEnv *env, const char *name, const char *msg); + +#define BUFF_SIZE (1024) + +/* Inaccessible static: skipBuffer */ +/* + * Class: SpawnerInputStream + * Method: read0 + * Signature: (I)I + */ +#ifdef __cplusplus +extern "C" +#endif +JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerInputStream_read0 + (JNIEnv * env, jobject proc, jint fd, jbyteArray buf, jint len) +{ + jbyte tmpBuf[BUFF_SIZE]; + int nBuffOffset = 0; +#ifdef DEBUG_MONITOR + _TCHAR buffer[1000]; +#endif + OVERLAPPED overlapped; + overlapped.Offset = 0; + overlapped.OffsetHigh = 0; + overlapped.hEvent = CreateEvent(NULL, // no security attribute + TRUE, // manual-reset event + TRUE, // initial state = signaled + NULL); // unnamed event object + + if(NULL == overlapped.hEvent) { + char * lpMsgBuf; + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (wchar_t *) &lpMsgBuf, + 0, + NULL + ); + + ThrowByName(env, "java/io/IOException", lpMsgBuf); + // Free the buffer. + LocalFree( lpMsgBuf ); + } + +#ifdef DEBUG_MONITOR +#ifdef READ_REPORT + _stprintf(buffer, _T("Start read %i\n"), fd); + OutputDebugStringW(buffer); +#endif +#endif + + while(len > nBuffOffset) + { + DWORD nNumberOfBytesToRead = min(len - nBuffOffset, BUFF_SIZE); + DWORD nNumberOfBytesRead; + if(0 == ReadFile((HANDLE)fd, tmpBuf, nNumberOfBytesToRead, &nNumberOfBytesRead, &overlapped )) + { + int err = GetLastError(); + + if(err == ERROR_IO_PENDING) + { + // asynchronous i/o is still in progress + // check on the results of the asynchronous read + if(GetOverlappedResult((HANDLE)fd, &overlapped, + &nNumberOfBytesRead, TRUE)) + err = 0; + // if there was a problem ... + else + err = GetLastError(); + } + if(err == ERROR_BROKEN_PIPE) // Pipe was closed + break; + if(err != 0) + { + char * lpMsgBuf; +#ifdef DEBUG_MONITOR + _stprintf(buffer, _T("Read failed - %i, error %i\n"), fd, err); + OutputDebugStringW(buffer); +#endif + if(err != ERROR_MORE_DATA) // Otherwise error means just that there are more data + { // than buffer can accept + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (wchar_t *) &lpMsgBuf, + 0, + NULL + ); + + ThrowByName(env, "java/io/IOException", lpMsgBuf); + LocalFree( lpMsgBuf ); + nBuffOffset = 0; + break; + } + else + { + // buffer overflow? + // according to msdn this happens in message read mode only +#ifdef DEBUG_MONITOR + _stprintf(buffer, _T("Buffer full - %i, bytes read: %i\n"), fd, nNumberOfBytesRead); + OutputDebugStringW(buffer); +#endif + // nNumberOfBytesRead can be 0 here for unknown reason (bug 269223) + nNumberOfBytesRead = nNumberOfBytesToRead; + } + } + } + if(nNumberOfBytesRead > 0) + (*env)->SetByteArrayRegion(env, buf, nBuffOffset, nNumberOfBytesRead, tmpBuf); + else + break; + nBuffOffset += nNumberOfBytesRead; + if(nNumberOfBytesRead != nNumberOfBytesToRead) + break; + else + { + // Is there data left in the pipe? + DWORD bytesAvailable = 0; + if (!PeekNamedPipe((HANDLE)fd, NULL, 0, NULL, &bytesAvailable, NULL) + || bytesAvailable == 0) + // No bytes left + break; + } + } + CloseHandle(overlapped.hEvent); +#ifdef DEBUG_MONITOR +#ifdef READ_REPORT + _stprintf(buffer, _T("End read %i - bytes read: %d\n"), fd, nBuffOffset); + OutputDebugStringW(buffer); +#endif +#endif + return nBuffOffset; // This is a real full readed length + +} + +/* + * Class: SpawnerInputStream + * Method: close0 + * Signature: (I)I + */ +#ifdef __cplusplus +extern "C" +#endif +JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerInputStream_close0 + (JNIEnv * env, jobject proc, jint fd) +{ + int rc; +#ifdef DEBUG_MONITOR + _TCHAR buffer[1000]; + _stprintf(buffer, _T("Close %i\n"), fd); + OutputDebugStringW(buffer); +#endif + rc = (CloseHandle((HANDLE)fd) ? 0 : -1); +#ifdef DEBUG_MONITOR + _stprintf(buffer, _T("Closed %i\n"), fd); + OutputDebugStringW(buffer); +#endif + return (rc ? GetLastError() : 0); +} + +#ifdef __cplusplus +extern "C" +#endif +JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerInputStream_available0 + (JNIEnv * env, jobject proc, jint fd) +{ + DWORD nAvail = 0; + + if (0 == PeekNamedPipe((HANDLE)fd, NULL, 0, NULL, &nAvail, NULL)) { + // error + return 0; + } + return nAvail; +} + +/* + * Class: SpawnerOutputStream + * Method: write0 + * Signature: (I[BI)I + */ +#ifdef __cplusplus +extern "C" +#endif +JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerOutputStream_write0 + (JNIEnv * env, jobject proc, jint fd, jbyteArray buf, jint len) +{ + jbyte tmpBuf[BUFF_SIZE]; + int nBuffOffset = 0; + + + while(len > nBuffOffset) + { + DWORD nNumberOfBytesToWrite = min(len - nBuffOffset, BUFF_SIZE); + DWORD nNumberOfBytesWritten; + (*env)->GetByteArrayRegion(env, buf, nBuffOffset, nNumberOfBytesToWrite, tmpBuf); + if(0 == WriteFile((HANDLE)fd, tmpBuf, nNumberOfBytesToWrite, &nNumberOfBytesWritten, NULL)) + { + char * lpMsgBuf; + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (wchar_t *) &lpMsgBuf, + 0, + NULL + ); + + ThrowByName(env, "java/io/IOException", lpMsgBuf); + LocalFree( lpMsgBuf ); + return 0; + } + nBuffOffset += nNumberOfBytesWritten; + } + return 0; +} + +/* + * Class: SpawnerOutputStream + * Method: close0 + * Signature: (I)I + */ +#ifdef __cplusplus +extern "C" +#endif +JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerOutputStream_close0 + (JNIEnv * env, jobject proc, jint fd) +{ + int rc; +#ifdef DEBUG_MONITOR + _TCHAR buffer[1000]; + _stprintf(buffer, _T("Close %i\n"), fd); + OutputDebugStringW(buffer); +#endif + FlushFileBuffers((HANDLE)fd); + rc = (CloseHandle((HANDLE)fd) ? 0 : -1); +#ifdef DEBUG_MONITOR + _stprintf(buffer, _T("Closed %i\n"), fd); + OutputDebugStringW(buffer); +#endif + return (rc ? GetLastError() : 0); +} diff --git a/core/org.eclipse.cdt.core.native/native_src/win/raise.c b/core/org.eclipse.cdt.core.native/native_src/win/raise.c new file mode 100644 index 00000000000..b908b159d0b --- /dev/null +++ b/core/org.eclipse.cdt.core.native/native_src/win/raise.c @@ -0,0 +1,194 @@ +/******************************************************************************* + * Copyright (c) 2002, 2011 QNX Software Systems 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: + * QNX Software Systems - initial API and implementation + * + * raise.c + * + * This is a part of JNI implementation of spawner + *******************************************************************************/ +#include <jni.h> +#include <windows.h> + +#include "Spawner.h" + +extern void JNICALL ThrowByName(JNIEnv *env, const char *name, const char *msg); + + +static HWND consoleHWND; + + +///////////////////////////////////////////////////////////////////////////////////// +// Check if window is a console of process with pid +// Arguments: +// hwnd - window handler +// arg - process PID +// Return : TRUE if yes +///////////////////////////////////////////////////////////////////////////////////// +static BOOL CALLBACK +find_child_console (HWND hwnd, LPARAM arg) +{ + DWORD thread_id; + DWORD process_id; + DWORD pid = arg; + + thread_id = GetWindowThreadProcessId (hwnd, &process_id); + if (process_id == pid) + { + wchar_t window_class[32]; + + GetClassName (hwnd, window_class, sizeof (window_class)); + if (wcscmp (window_class, L"ConsoleWindowClass") == 0) + { + consoleHWND = hwnd; + return FALSE; + } + } + /* keep looking */ + return TRUE; +} + +// Need to declare this Win32 prototype ourselves. _WIN32_WINNT is getting +// defined to a Windows NT value, thus we don't get this. Can't assume +// we're running on XP, anyway (or can we by now?) +#if (_WIN32_WINNT < 0x0501) || defined(_MSC_VER) +typedef BOOL (WINAPI *DebugBreakProcessFunc)(HANDLE); +#endif + +///////////////////////////////////////////////////////////////////////////////////// +// Called to interrupt a process that we didn't launch (and thus does not share our +// console). Windows XP introduced the function 'DebugBreakProcess', which allows +// a process to interrupt another process even if if the two do not share a console. +// If we're running on 2000 or earlier, we have to resort to simulating a CTRL-C +// in the console by firing keyboard events. This will work only if the process +// has its own console. That means, e.g., the process should have been started at +// the cmdline with 'start myprogram.exe' instead of 'myprogram.exe'. +// +// Arguments: +// pid - process' pid +// Return : 0 if OK or error code +///////////////////////////////////////////////////////////////////////////////////// +int interruptProcess(int pid) +{ + // See if DebugBreakProcess is available (XP and beyond) + HMODULE hmod = LoadLibrary(L"Kernel32.dll"); + if (hmod != NULL) + { + BOOL success = FALSE; + FARPROC procaddr = GetProcAddress(hmod, "DebugBreakProcess"); + if (procaddr != NULL) + { + HANDLE proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)pid); + if (proc != NULL) + { + typedef BOOL WINAPI (*DebugBreakProcessFunc)(HANDLE); + DebugBreakProcessFunc pDebugBreakProcess = (DebugBreakProcessFunc)procaddr; + success = (*pDebugBreakProcess)(proc); + CloseHandle(proc); + } + } + FreeLibrary(hmod); + hmod = NULL; + + if (success) + return 0; // 0 == OK; if not, try old-school way + } + +#ifdef DEBUG_MONITOR + _TCHAR buffer[1000]; +#endif + int rc = 0; + consoleHWND = NULL; + +#ifdef DEBUG_MONITOR + _stprintf(buffer, _T("Try to interrupt process %i\n"), pid); + OutputDebugStringW(buffer); +#endif + // Find console + EnumWindows (find_child_console, (LPARAM) pid); + + if(NULL != consoleHWND) // Yes, we found out it + { + // We are going to switch focus to console, + // send Ctrl-C and then restore focus + BYTE control_scan_code = (BYTE) MapVirtualKey (VK_CONTROL, 0); + /* Fake Ctrl-C for SIGINT, and Ctrl-Break for SIGQUIT. */ + BYTE vk_c_code = 'C'; + BYTE vk_break_code = VK_CANCEL; + BYTE c_scan_code = (BYTE) MapVirtualKey (vk_c_code, 0); + BYTE break_scan_code = (BYTE) MapVirtualKey (vk_break_code, 0); + HWND foreground_window; + + + foreground_window = GetForegroundWindow (); + if (foreground_window) + { + /* NT 5.0, and apparently also Windows 98, will not allow + a Window to be set to foreground directly without the + user's involvement. The workaround is to attach + ourselves to the thread that owns the foreground + window, since that is the only thread that can set the + foreground window. */ + DWORD foreground_thread, child_thread; + foreground_thread = + GetWindowThreadProcessId (foreground_window, NULL); + if (foreground_thread == GetCurrentThreadId () + || !AttachThreadInput (GetCurrentThreadId (), + foreground_thread, TRUE)) + foreground_thread = 0; + + child_thread = GetWindowThreadProcessId (consoleHWND, NULL); + if (child_thread == GetCurrentThreadId () + || !AttachThreadInput (GetCurrentThreadId (), + child_thread, TRUE)) + child_thread = 0; + + /* Set the foreground window to the child. */ + if (SetForegroundWindow (consoleHWND)) + { + if(0 != break_scan_code) { + /* Generate keystrokes as if user had typed Ctrl-Break */ + keybd_event (VK_CONTROL, control_scan_code, 0, 0); + keybd_event (vk_break_code, break_scan_code, KEYEVENTF_EXTENDEDKEY, 0); + keybd_event (vk_break_code, break_scan_code, + KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0); + keybd_event (VK_CONTROL, control_scan_code, KEYEVENTF_KEYUP, 0); + } + + /* Sleep for a bit to give time for respond */ + Sleep (100); + + SetForegroundWindow (foreground_window); + } + /* Detach from the foreground and child threads now that + the foreground switching is over. */ + if (foreground_thread) + AttachThreadInput (GetCurrentThreadId (), + foreground_thread, FALSE); + if (child_thread) + AttachThreadInput (GetCurrentThreadId (), + child_thread, FALSE); +#ifdef DEBUG_MONITOR + _stprintf(buffer, _T("Sent Ctrl-C & Ctrl-Break to process %i\n"), pid); + OutputDebugStringW(buffer); +#endif + } + } +#ifdef DEBUG_MONITOR + else { + _stprintf(buffer, _T("Cannot find console for process %i\n"), pid); + OutputDebugStringW(buffer); + } +#endif + + return rc; +} + diff --git a/core/org.eclipse.cdt.core.native/native_src/win/spawner.c b/core/org.eclipse.cdt.core.native/native_src/win/spawner.c new file mode 100644 index 00000000000..ba3e27f2c44 --- /dev/null +++ b/core/org.eclipse.cdt.core.native/native_src/win/spawner.c @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2002, 2007 QNX Software Systems 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: + * QNX Software Systems - initial API and implementation + * + * spawner.c + * + * This is a part of JNI implementation of spawner + *******************************************************************************/ + +#include <tchar.h> +#include <windows.h> + +#include "Spawner.h" + +CRITICAL_SECTION cs; + + +wchar_t path[MAX_PATH + 1] = {_T('\0') }; // Directory where spawner.dll is located + +#if __cplusplus +extern "C" +#endif +BOOL APIENTRY DllMain( HINSTANCE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + { + wchar_t * p; + InitializeCriticalSection(&cs); + GetModuleFileNameW(hModule, path, MAX_PATH); + p = wcsrchr(path, _T('\\')); + if(NULL != p) + *(p + 1) = _T('\0'); + else + wcscat(path, L"\\"); + } + break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + case DLL_PROCESS_DETACH: + DeleteCriticalSection(&cs); + break; + } + return TRUE; +} + diff --git a/core/org.eclipse.cdt.core.native/native_src/win/starter.c b/core/org.eclipse.cdt.core.native/native_src/win/starter.c new file mode 100644 index 00000000000..6284f75586f --- /dev/null +++ b/core/org.eclipse.cdt.core.native/native_src/win/starter.c @@ -0,0 +1,527 @@ +/******************************************************************************* + * Copyright (c) 2002, 2015 QNX Software Systems 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: + * QNX Software Systems - initial API and implementation + * Wind River Systems, Inc. + * + * starter.cpp + * + * This is a small utility for windows spawner + *******************************************************************************/ + +#define STRICT +#define _WIN32_WINNT 0x0500 +#include <windows.h> +#include <process.h> +#include <tchar.h> +#include <stdio.h> +#include <psapi.h> +#include <stdbool.h> + +//#define DEBUG_MONITOR +#define MAX_CMD_LINE_LENGTH (2049) +#define PIPE_NAME_LENGTH 100 + +int copyTo(wchar_t * target, const wchar_t * source, int cpyLength, + int availSpace); +void DisplayErrorMessage(); + +//BOOL KillProcessEx(DWORD dwProcessId); // Handle of the process + +/////////////////////////////////////////////////////////////////////////////// +BOOL WINAPI HandlerRoutine( DWORD dwCtrlType) // control signal type +{ + BOOL ret = TRUE; + switch(dwCtrlType) + { + case CTRL_C_EVENT: + break; + case CTRL_BREAK_EVENT: + break; + case CTRL_CLOSE_EVENT: + ret = FALSE; + break; + case CTRL_LOGOFF_EVENT: + ret = FALSE; + break; + case CTRL_SHUTDOWN_EVENT: + ret = FALSE; + break; + default: + break; + } + return ret; +} + +// The default here means we haven't checked yet +// i.e. cygwin is true but the bin dir hasn't been captured +wchar_t * cygwinBin = NULL; +bool _isCygwin = true; + +bool isCygwin(HANDLE process) { + // Have we checked before? + if (cygwinBin != NULL || !_isCygwin) + return _isCygwin; + + // See if this process loaded cygwin, need a different SIGINT for them + HMODULE mods[1024]; + DWORD needed; + if (EnumProcessModules(process, mods, sizeof(mods), &needed)) { + int i; + needed /= sizeof(HMODULE); + for (i = 0; i < needed; ++i ) { + wchar_t modName[MAX_PATH]; + if (GetModuleFileNameEx(process, mods[i], modName, MAX_PATH)) { + wchar_t * p = wcsrchr(modName, L'\\'); + if (p) { + *p = 0; // Null terminate there for future reference + if (!wcscmp(++p, L"cygwin1.dll")) { + _isCygwin = true; + // Store away the bind dir + cygwinBin = wcsdup(modName); + return _isCygwin; + } + } + } + } + } + + _isCygwin = false; + return _isCygwin; +} + +bool runCygwinCommand(wchar_t * command) { + wchar_t cygcmd[1024]; + swprintf(cygcmd, sizeof(cygcmd)/sizeof(cygcmd[0]), L"%s\\%s", cygwinBin, command); + + STARTUPINFO si; + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + PROCESS_INFORMATION pi; + ZeroMemory(&pi, sizeof(pi)); + if (CreateProcess(NULL, cygcmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + return true; + } else if (CreateProcess(NULL, command, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + return true; + } + return false; +} + +void ensureSize(wchar_t** ptr, int* psize, int requiredLength) { + int size= *psize; + if (requiredLength > size) { + size= 2*size; + if (size < requiredLength) { + size= requiredLength; + } + *ptr= (wchar_t *)realloc(*ptr, size * sizeof(wchar_t)); + if (NULL == *ptr) { + *psize= 0; + } else { + *psize= size; + } + } +} + +int main() { + + int argc; + wchar_t ** argv = CommandLineToArgvW(GetCommandLine(), &argc); + + // Make sure that we've been passed the right number of arguments + if (argc < 8) { + _tprintf(_T("Usage: %s (four inheritable event handles) (CommandLineToSpawn)\n"), + argv[0]); + return(0); + } + + // Construct the full command line + int nCmdLineLength= MAX_CMD_LINE_LENGTH; + wchar_t * szCmdLine= (wchar_t *)malloc(nCmdLineLength * sizeof(wchar_t)); + szCmdLine[0]= 0; + int nPos = 0; + + for(int i = 8; i < argc; ++i) + { + int nCpyLen; + int len= wcslen(argv[i]); + int requiredSize= nPos+len+2; + if (requiredSize > 32*1024) { +#ifdef DEBUG_MONITOR + OutputDebugStringW(_T("Command line too long!\n")); +#endif + return 0; + } + ensureSize(&szCmdLine, &nCmdLineLength, requiredSize); + if (NULL == szCmdLine) { +#ifdef DEBUG_MONITOR + OutputDebugStringW(_T("Not enough memory to build cmd line!\n")); +#endif + return 0; + } + if(0 > (nCpyLen = copyTo(szCmdLine + nPos, argv[i], len, nCmdLineLength - nPos))) + { +#ifdef DEBUG_MONITOR + OutputDebugStringW(_T("Not enough space to build command line\n")); +#endif + return 0; + } + nPos += nCpyLen; + szCmdLine[nPos] = _T(' '); + ++nPos; + } + szCmdLine[nPos] = _T('\0'); + + STARTUPINFOW si = {sizeof(si)}; + PROCESS_INFORMATION pi = {0}; + DWORD dwExitCode = 0; +#ifdef DEBUG_MONITOR + int currentPID = GetCurrentProcessId(); + wchar_t buffer[MAX_CMD_LINE_LENGTH]; +#endif + + BOOL exitProc = FALSE; + HANDLE waitEvent = OpenEventW(EVENT_ALL_ACCESS, TRUE, argv[4]); + HANDLE h[5]; + h[0] = OpenEventW(EVENT_ALL_ACCESS, TRUE, argv[3]); // simulated SIGINT (CTRL-C or Cygwin 'kill -SIGINT') +// h[1] we reserve for the process handle + h[2] = OpenEventW(EVENT_ALL_ACCESS, TRUE, argv[5]); // simulated SIGTERM + h[3] = OpenEventW(EVENT_ALL_ACCESS, TRUE, argv[6]); // simulated SIGKILL + h[4] = OpenEventW(EVENT_ALL_ACCESS, TRUE, argv[7]); // CTRL-C, in all cases + + SetConsoleCtrlHandler(HandlerRoutine, TRUE); + + int parentPid = wcstol(argv[1], NULL, 10); + int nCounter = wcstol(argv[2], NULL, 10); + wchar_t inPipeName[PIPE_NAME_LENGTH]; + wchar_t outPipeName[PIPE_NAME_LENGTH]; + wchar_t errPipeName[PIPE_NAME_LENGTH]; + + swprintf(inPipeName, sizeof(inPipeName)/sizeof(inPipeName[0]), L"\\\\.\\pipe\\stdin%08i%010i", parentPid, nCounter); + swprintf(outPipeName, sizeof(outPipeName)/sizeof(outPipeName[0]), L"\\\\.\\pipe\\stdout%08i%010i", parentPid, nCounter); + swprintf(errPipeName, sizeof(errPipeName)/sizeof(errPipeName[0]), L"\\\\.\\pipe\\stderr%08i%010i", parentPid, nCounter); +#ifdef DEBUG_MONITOR + swprintf(buffer, _T("Pipes: %s, %s, %s\n"), inPipeName, outPipeName, errPipeName); + OutputDebugStringW(buffer); +#endif + + HANDLE stdHandles[3]; + + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = TRUE; + sa.lpSecurityDescriptor = NULL; + + if((INVALID_HANDLE_VALUE == (stdHandles[0] = CreateFileW(inPipeName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, &sa))) || + (INVALID_HANDLE_VALUE == (stdHandles[1] = CreateFileW(outPipeName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, &sa))) || + (INVALID_HANDLE_VALUE == (stdHandles[2] = CreateFileW(errPipeName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, &sa)))) + { +#ifdef DEBUG_MONITOR + swprintf(buffer, sizeof(buffer)/sizeof(buffer[0]), _T("Failed to open pipe %i, %i, %i: %i\n"), stdHandles[0], stdHandles[1], stdHandles[2], GetLastError()); + OutputDebugStringW(buffer); +#endif + CloseHandle(stdHandles[0]); + CloseHandle(stdHandles[1]); + CloseHandle(stdHandles[2]); + return -1;; + } + SetHandleInformation(stdHandles[0], HANDLE_FLAG_INHERIT, TRUE); + SetHandleInformation(stdHandles[1], HANDLE_FLAG_INHERIT, TRUE); + SetHandleInformation(stdHandles[2], HANDLE_FLAG_INHERIT, TRUE); + + if(!SetStdHandle(STD_INPUT_HANDLE, stdHandles[0]) || + !SetStdHandle(STD_OUTPUT_HANDLE, stdHandles[1]) || + !SetStdHandle(STD_ERROR_HANDLE, stdHandles[2])) { +#ifdef DEBUG_MONITOR + swprintf(buffer, sizeof(buffer)/sizeof(buffer[0]), _T("Failed to reassign standard streams: %i\n"), GetLastError()); + OutputDebugStringW(buffer); +#endif + CloseHandle(stdHandles[0]); + CloseHandle(stdHandles[1]); + CloseHandle(stdHandles[2]); + return -1;; + } + +#ifdef DEBUG_MONITOR_DETAILS + wchar_t * lpvEnv = GetEnvironmentStringsW(); + + // If the returned pointer is NULL, exit. + if (lpvEnv == NULL) + OutputDebugStringW(_T("Cannot Read Environment\n")); + else { + // Variable strings are separated by NULL byte, and the block is + // terminated by a NULL byte. + + OutputDebugStringW(_T("Starter: Environment\n")); + for (wchar_t * lpszVariable = (wchar_t *) lpvEnv; *lpszVariable; lpszVariable+=wcslen(lpszVariable) + 1) { + swprintf(buffer, sizeof(buffer)/sizeof(buffer[0]), _T("%s\n"), lpszVariable); + OutputDebugStringW(buffer); + } + + FreeEnvironmentStringsW(lpvEnv); + } +#endif +#ifdef DEBUG_MONITOR + swprintf(buffer, sizeof(buffer)/sizeof(buffer[0]), _T("Starting: %s\n"), szCmdLine); + OutputDebugStringW(buffer); +#endif + // Create job object + HANDLE hJob = CreateJobObject(NULL, NULL); + if (hJob != NULL) { + // Configure job to + // - terminate all associated processes when the last handle to it is closed + // - allow child processes to break away from the job. + JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobInfo; + ZeroMemory(&jobInfo, sizeof(jobInfo)); + jobInfo.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_LIMIT_BREAKAWAY_OK; + if (!SetInformationJobObject(hJob, JobObjectExtendedLimitInformation, &jobInfo, sizeof(jobInfo))) { +#ifdef DEBUG_MONITOR + OutputDebugStringW(_T("Cannot set job information\n")); + DisplayErrorMessage(); +#endif + } + } else { +#ifdef DEBUG_MONITOR + OutputDebugStringW(_T("Cannot create job object\n")); + DisplayErrorMessage(); +#endif + } + // Spawn the other processes as part of this Process Group + // If this process is already part of a job, the flag CREATE_BREAKAWAY_FROM_JOB + // makes the child process detach from the job, such that we can assign it + // to our own job object. + BOOL f = CreateProcessW(NULL, szCmdLine, NULL, NULL, TRUE, + CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, &si, &pi); + // If breaking away from job is not permitted, retry without breakaway flag + if (!f) + f = CreateProcessW(NULL, szCmdLine, NULL, NULL, TRUE, + 0, NULL, NULL, &si, &pi); + + // We don't need them any more + CloseHandle(stdHandles[0]); + CloseHandle(stdHandles[1]); + CloseHandle(stdHandles[2]); + + if (f) { +#ifdef DEBUG_MONITOR + swprintf(buffer, sizeof(buffer)/sizeof(buffer[0]), _T("Process %i started\n"), pi.dwProcessId); + OutputDebugStringW(buffer); +#endif + SetEvent(waitEvent); // Means that process has been spawned + CloseHandle(pi.hThread); + h[1] = pi.hProcess; + + if(NULL != hJob) { + if(!AssignProcessToJobObject(hJob, pi.hProcess)) { +#ifdef DEBUG_MONITOR + swprintf(buffer, sizeof(buffer)/sizeof(buffer[0]), _T("Cannot assign process %i to a job\n"), pi.dwProcessId); + OutputDebugStringW(buffer); + DisplayErrorMessage(); +#endif + } + } + + while(!exitProc) + { + // Wait for the spawned-process to die or for the event + // indicating that the processes should be forcibly killed. + DWORD event = WaitForMultipleObjects(5, h, FALSE, INFINITE); + switch (event) + { + case WAIT_OBJECT_0 + 0: // SIGINT + case WAIT_OBJECT_0 + 4: // CTRL-C +#ifdef DEBUG_MONITOR + swprintf(buffer, sizeof(buffer)/sizeof(buffer[0]), _T("starter (PID %i) received CTRL-C event\n"), currentPID); + OutputDebugStringW(buffer); +#endif + if ((event == (WAIT_OBJECT_0 + 0)) && isCygwin(h[1])) { + // Need to issue a kill command + wchar_t kill[1024]; + swprintf(kill, sizeof(kill)/sizeof(kill[0]), L"kill -SIGINT %d", pi.dwProcessId); + if (!runCygwinCommand(kill)) { + // fall back to console event + GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); + } + } else { + GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); + } + + SetEvent(waitEvent); + break; + + case WAIT_OBJECT_0 + 1: // App terminated normally + // Make it's exit code our exit code +#ifdef DEBUG_MONITOR + swprintf(buffer, sizeof(buffer)/sizeof(buffer[0]), _T("starter: launched process has been terminated(PID %i)\n"), + pi.dwProcessId); + OutputDebugStringW(buffer); +#endif + GetExitCodeProcess(pi.hProcess, &dwExitCode); + exitProc = TRUE; + break; + + // Terminate and Kill behavior differ only for cygwin processes, where + // we use the cygwin 'kill' command. We send a SIGKILL in one case, + // SIGTERM in the other. For non-cygwin processes, both requests + // are treated exactly the same + case WAIT_OBJECT_0 + 2: // TERM + case WAIT_OBJECT_0 + 3: // KILL + { + const wchar_t* signal = (event == WAIT_OBJECT_0 + 2) ? L"TERM" : L"KILL"; +#ifdef DEBUG_MONITOR + swprintf(buffer, sizeof(buffer)/sizeof(buffer[0]), _T("starter received %s event (PID %i)\n"), signal, currentPID); + OutputDebugStringW(buffer); +#endif + if (isCygwin(h[1])) { + // Need to issue a kill command + wchar_t kill[1024]; + swprintf(kill, sizeof(kill)/sizeof(kill[0]), L"kill -%s %d", signal, pi.dwProcessId); + if (!runCygwinCommand(kill)) { + // fall back to console event + GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); + } + } else { + GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); + } + + SetEvent(waitEvent); + + if(NULL != hJob) { + if(!TerminateJobObject(hJob, (DWORD)-1)) { +#ifdef DEBUG_MONITOR + OutputDebugStringW(_T("Cannot terminate job\n")); + DisplayErrorMessage(); +#endif + } + } + + // Note that we keep trucking until the child process terminates (case WAIT_OBJECT_0 + 1) + break; + } + + default: + // Unexpected code +#ifdef DEBUG_MONITOR + DisplayErrorMessage(); +#endif + exitProc = TRUE; + break; + } + + } + } else { +#ifdef DEBUG_MONITOR + swprintf(buffer, sizeof(buffer)/sizeof(buffer[0]), _T("Cannot start: %s\n"), szCmdLine); + OutputDebugStringW(buffer); + + DisplayErrorMessage(); +#endif + } + + if (NULL != szCmdLine) + { + free(szCmdLine); + } + + CloseHandle(waitEvent); + CloseHandle(h[0]); + CloseHandle(h[1]); + CloseHandle(h[2]); + CloseHandle(h[3]); + CloseHandle(h[4]); + + return(dwExitCode); +} + +///////////////////////////////////////////////////////////////////////////////////// +// Use this utility program to process correctly quotation marks in the command line +// Arguments: +// target - string to copy to +// source - string to copy from +// cpyLength - copy length +// availSpace - size of the target buffer +// Return :number of bytes used in target, or -1 in case of error +///////////////////////////////////////////////////////////////////////////////////// +int copyTo(wchar_t * target, const wchar_t * source, int cpyLength, + int availSpace) { + BOOL bSlash = FALSE; + int i = 0, j = 0; + int totCpyLength = cpyLength; + +#define QUOTATION_DO 0 +#define QUOTATION_DONE 1 +#define QUOTATION_NONE 2 + + int nQuotationMode = 0; + if (availSpace <= cpyLength) // = to reserve space for '\0' + return -1; + + if ((_T('\"') == *source) && (_T('\"') == *(source + cpyLength - 1))) { + // Already done + nQuotationMode = QUOTATION_DONE; + } else if (wcschr(source, _T(' '))== NULL) { + // No reason to quotate term becase it doesn't have embedded spaces + nQuotationMode = QUOTATION_NONE; + } else { + // Needs to be quotated + nQuotationMode = QUOTATION_DO; + *target = _T('\"'); + ++j; + } + + for (; i < cpyLength; ++i, ++j) { + if (source[i] == _T('\\')) + bSlash = TRUE; + else + // Don't escape embracing quotation marks + if ((source[i] == _T('\"')) && !((nQuotationMode == QUOTATION_DONE) && ((i == 0) || (i == (cpyLength - 1))) )) { + if (!bSlash) { + if (j == availSpace) + return -1; + target[j] = _T('\\'); + ++j; + } + bSlash = FALSE; + } else + bSlash = FALSE; + + if (j == availSpace) + return -1; + target[j] = source[i]; + } + + if (nQuotationMode == QUOTATION_DO) { + if (j == availSpace) + return -1; + target[j] = _T('\"'); + ++j; + } + return j; +} + +void DisplayErrorMessage() { + wchar_t * lpMsgBuf; + FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER |FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (wchar_t *) &lpMsgBuf, 0, NULL); + OutputDebugStringW(lpMsgBuf); + // Free the buffer. + LocalFree(lpMsgBuf); +} + +//////////////////////////////// End of File ////////////////////////////////// diff --git a/core/org.eclipse.cdt.core.native/pom.xml b/core/org.eclipse.cdt.core.native/pom.xml new file mode 100644 index 00000000000..02bc3bf9137 --- /dev/null +++ b/core/org.eclipse.cdt.core.native/pom.xml @@ -0,0 +1,108 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (c) 2018 Red Hat, 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: + Mat Booth (Red Hat) - initial implementation +--> +<project + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.eclipse.cdt</groupId> + <artifactId>cdt-parent</artifactId> + <version>10.0.0-SNAPSHOT</version> + <relativePath>../../pom.xml</relativePath> + </parent> + + <version>6.0.0-SNAPSHOT</version> + <artifactId>org.eclipse.cdt.core.native</artifactId> + <packaging>eclipse-plugin</packaging> + + <profiles> + <profile> + <id>build-native.all</id> + <activation> + <property> + <name>native</name> + <value>all</value> + </property> + </activation> + <build> + <plugins> + <plugin> + <artifactId>maven-antrun-plugin</artifactId> + <executions> + <execution> + <id>natives</id> + <phase>process-resources</phase> + <configuration> + <target> + <exec executable="make" newenvironment="false" failOnError="true" dir="./native_src"> + <arg value="rebuild" /> + </exec> + </target> + </configuration> + <goals> + <goal>run</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>build-native.docker</id> + <activation> + <property> + <name>native</name> + <value>docker</value> + </property> + </activation> + <build> + <plugins> + <plugin> + <artifactId>maven-antrun-plugin</artifactId> + <executions> + <execution> + <id>natives</id> + <phase>process-resources</phase> + <configuration> + <target> + <exec executable="docker" newenvironment="false" failOnError="true"> + <!-- docker run -\-rm -t -v $(git rev-parse -\-show-toplevel):/work -w /work/$(git rev-parse -\-show-prefix) quay.io/eclipse-cdt/cdt-infra-eclipse-full:latest make -C native_src rebuild --> + <arg value="run" /> + <arg value="--rm" /> + <arg value="-t" /> + <arg value="-v" /> + <arg value="${project.basedir}/../..:/work" /> + <arg value="-w" /> + <arg value="/work/core/org.eclipse.cdt.core.native" /> + <arg value="quay.io/eclipse-cdt/cdt-infra-eclipse-full:latest" /> + <arg value="make" /> + <arg value="-C" /> + <arg value="native_src" /> + <arg value="rebuild" /> + </exec> + </target> + </configuration> + <goals> + <goal>run</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + </profiles> +</project> |