jruby-launcher 1.1.1-java → 1.1.2-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,143 +1,143 @@
1
- /*
2
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
- *
4
- * Copyright 2009-2012 JRuby Team (www.jruby.org).
5
- * Copyright 1997-2008 Sun Microsystems, Inc. All rights reserved.
6
- *
7
- * The contents of this file are subject to the terms of either the GNU
8
- * General Public License Version 2 only ("GPL") or the Common
9
- * Development and Distribution License("CDDL") (collectively, the
10
- * "License"). You may not use this file except in compliance with the
11
- * License. You can obtain a copy of the License at
12
- * http://www.netbeans.org/cddl-gplv2.html
13
- * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
14
- * specific language governing permissions and limitations under the
15
- * License. When distributing the software, include this License Header
16
- * Notice in each file and include the License file at
17
- * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
18
- * particular file as subject to the "Classpath" exception as provided
19
- * by Sun in the GPL Version 2 section of the License file that
20
- * accompanied this code. If applicable, add the following below the
21
- * License Header, with the fields enclosed by brackets [] replaced by
22
- * your own identifying information:
23
- * "Portions Copyrighted [year] [name of copyright owner]"
24
- *
25
- * Contributor(s):
26
- *
27
- * The Original Software is NetBeans. The Initial Developer of the Original
28
- * Software is Sun Microsystems, Inc. Portions Copyright 1997-2008 Sun
29
- * Microsystems, Inc. All Rights Reserved.
30
- *
31
- * If you wish your version of this file to be governed by only the CDDL
32
- * or only the GPL Version 2, indicate your decision by adding
33
- * "[Contributor] elects to include this software in this distribution
34
- * under the [CDDL or GPL Version 2] license." If you do not indicate a
35
- * single choice of license, a recipient has the option to distribute
36
- * your version of this file under either the CDDL, the GPL Version 2 or
37
- * to extend the choice of license to its licensees as provided above.
38
- * However, if you add GPL Version 2 code and therefore, elected the GPL
39
- * Version 2 license, then the option applies only if the new code is
40
- * made subject to such option by the copyright holder.
41
- *
42
- * Author: Tomas Holy
43
- */
44
-
45
- #ifndef _JVMLAUNCHER_H
46
- #define _JVMLAUNCHER_H
47
-
48
- #include <windows.h>
49
- #include <string>
50
- #include <list>
51
- #include "jni.h"
52
- #include "utilsfuncs.h"
53
-
54
- class JvmLauncher {
55
- static const int MAX_ARGS_LEN = 32*1024;
56
-
57
- static const char *JDK_KEY;
58
- static const char *JRE_KEY;
59
- static const char *CUR_VERSION_NAME;
60
- static const char *JAVA_HOME_NAME;
61
- static const char *JAVA_BIN_DIR;
62
- static const char *JAVA_EXE_FILE;
63
- static const char *JAVAW_EXE_FILE;
64
- static const char *JAVA_CLIENT_DLL_FILE;
65
- static const char *JAVA_SERVER_DLL_FILE;
66
- static const char *JAVA_JRE_PREFIX;
67
- static const char *JNI_CREATEVM_FUNC;
68
-
69
- public:
70
- JvmLauncher();
71
- virtual ~JvmLauncher();
72
-
73
- bool initialize(const char *javaPathOrMinVersion);
74
- bool getJavaPath(std::string &path);
75
- bool start(const char *mainClassName, std::list<std::string> args, std::list<std::string> options, bool &separateProcess, DWORD *retCode);
76
-
77
- void setSuppressConsole(bool val) {
78
- suppressConsole = val;
79
- }
80
- void setJavaCmd(const std::string cmdPath);
81
-
82
- private:
83
- JvmLauncher(const JvmLauncher& orig);
84
-
85
- bool checkJava(std::string &javaPath, const char *version);
86
- bool findJava(const char *minJavaVersion);
87
- bool findJava(const char *javaKey, const char *prefix, const char *minJavaVersion);
88
- bool startOutProcJvm(const char *mainClassName, std::list<std::string> args, std::list<std::string> options, DWORD *retCode);
89
- bool startInProcJvm(const char *mainClassName, std::list<std::string> args, std::list<std::string> options);
90
- bool isVersionString(const char *str);
91
- bool canLoadJavaDll();
92
- bool findServerOption(std::list<std::string> &options);
93
-
94
- private:
95
- bool suppressConsole;
96
- std::string javaExePath;
97
- std::string javawExePath;
98
- std::string javaDllPath;
99
- std::string javaClientDllPath;
100
- std::string javaServerDllPath;
101
- std::string javaPath;
102
- std::string javaBinPath;
103
-
104
- class PrepareDllPath {
105
- public:
106
- PrepareDllPath(const char *dllDirectory)
107
- : setDllDirectory(0) {
108
- logMsg("PrepareDllPath: %s", dllDirectory);
109
- oldCurDir[0] = '\0';
110
-
111
- // SetDllDirectory is present since XP SP1, so we have to load it dynamically
112
- HINSTANCE hKernel32 = GetModuleHandle("kernel32");
113
- if (!hKernel32) {
114
- logErr(true, false, "Cannot load kernel32.");
115
- return;
116
- }
117
-
118
- LPFNSDD setDllDirectory = (LPFNSDD)GetProcAddress(hKernel32, "SetDllDirectoryA");
119
- if (setDllDirectory) {
120
- setDllDirectory(dllDirectory);
121
- } else {
122
- logErr(true, false, "Cannot find SetDllDirectoryA");
123
- }
124
- GetCurrentDirectory(MAX_PATH, oldCurDir);
125
- SetCurrentDirectory(dllDirectory);
126
- }
127
- ~PrepareDllPath() {
128
- if (setDllDirectory) {
129
- setDllDirectory(NULL);
130
- }
131
- if (oldCurDir[0]) {
132
- SetCurrentDirectory(oldCurDir);
133
- }
134
- }
135
- private:
136
- typedef BOOL (WINAPI *LPFNSDD)(LPCTSTR lpPathname);
137
- LPFNSDD setDllDirectory;
138
- char oldCurDir[MAX_PATH];
139
- };
140
- };
141
-
142
- #endif /* _JVMLAUNCHER_H */
143
-
1
+ /*
2
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
+ *
4
+ * Copyright 2009-2012 JRuby Team (www.jruby.org).
5
+ * Copyright 1997-2008 Sun Microsystems, Inc. All rights reserved.
6
+ *
7
+ * The contents of this file are subject to the terms of either the GNU
8
+ * General Public License Version 2 only ("GPL") or the Common
9
+ * Development and Distribution License("CDDL") (collectively, the
10
+ * "License"). You may not use this file except in compliance with the
11
+ * License. You can obtain a copy of the License at
12
+ * http://www.netbeans.org/cddl-gplv2.html
13
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
14
+ * specific language governing permissions and limitations under the
15
+ * License. When distributing the software, include this License Header
16
+ * Notice in each file and include the License file at
17
+ * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
18
+ * particular file as subject to the "Classpath" exception as provided
19
+ * by Sun in the GPL Version 2 section of the License file that
20
+ * accompanied this code. If applicable, add the following below the
21
+ * License Header, with the fields enclosed by brackets [] replaced by
22
+ * your own identifying information:
23
+ * "Portions Copyrighted [year] [name of copyright owner]"
24
+ *
25
+ * Contributor(s):
26
+ *
27
+ * The Original Software is NetBeans. The Initial Developer of the Original
28
+ * Software is Sun Microsystems, Inc. Portions Copyright 1997-2008 Sun
29
+ * Microsystems, Inc. All Rights Reserved.
30
+ *
31
+ * If you wish your version of this file to be governed by only the CDDL
32
+ * or only the GPL Version 2, indicate your decision by adding
33
+ * "[Contributor] elects to include this software in this distribution
34
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
35
+ * single choice of license, a recipient has the option to distribute
36
+ * your version of this file under either the CDDL, the GPL Version 2 or
37
+ * to extend the choice of license to its licensees as provided above.
38
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
39
+ * Version 2 license, then the option applies only if the new code is
40
+ * made subject to such option by the copyright holder.
41
+ *
42
+ * Author: Tomas Holy
43
+ */
44
+
45
+ #ifndef _JVMLAUNCHER_H
46
+ #define _JVMLAUNCHER_H
47
+
48
+ #include <windows.h>
49
+ #include <string>
50
+ #include <list>
51
+ #include "jni.h"
52
+ #include "utilsfuncs.h"
53
+
54
+ class JvmLauncher {
55
+ static const int MAX_ARGS_LEN = 32*1024;
56
+
57
+ static const char *JDK_KEY;
58
+ static const char *JRE_KEY;
59
+ static const char *CUR_VERSION_NAME;
60
+ static const char *JAVA_HOME_NAME;
61
+ static const char *JAVA_BIN_DIR;
62
+ static const char *JAVA_EXE_FILE;
63
+ static const char *JAVAW_EXE_FILE;
64
+ static const char *JAVA_CLIENT_DLL_FILE;
65
+ static const char *JAVA_SERVER_DLL_FILE;
66
+ static const char *JAVA_JRE_PREFIX;
67
+ static const char *JNI_CREATEVM_FUNC;
68
+
69
+ public:
70
+ JvmLauncher();
71
+ virtual ~JvmLauncher();
72
+
73
+ bool initialize(const char *javaPathOrMinVersion);
74
+ bool getJavaPath(std::string &path);
75
+ bool start(const char *mainClassName, std::list<std::string> args, std::list<std::string> options, bool &separateProcess, DWORD *retCode);
76
+
77
+ void setSuppressConsole(bool val) {
78
+ suppressConsole = val;
79
+ }
80
+ void setJavaCmd(const std::string cmdPath);
81
+
82
+ private:
83
+ JvmLauncher(const JvmLauncher& orig);
84
+
85
+ bool checkJava(std::string &javaPath, const char *version);
86
+ bool findJava(const char *minJavaVersion);
87
+ bool findJava(const char *javaKey, const char *prefix, const char *minJavaVersion);
88
+ bool startOutProcJvm(const char *mainClassName, std::list<std::string> args, std::list<std::string> options, DWORD *retCode);
89
+ bool startInProcJvm(const char *mainClassName, std::list<std::string> args, std::list<std::string> options);
90
+ bool isVersionString(const char *str);
91
+ bool canLoadJavaDll();
92
+ bool findServerOption(std::list<std::string> &options);
93
+
94
+ private:
95
+ bool suppressConsole;
96
+ std::string javaExePath;
97
+ std::string javawExePath;
98
+ std::string javaDllPath;
99
+ std::string javaClientDllPath;
100
+ std::string javaServerDllPath;
101
+ std::string javaPath;
102
+ std::string javaBinPath;
103
+
104
+ class PrepareDllPath {
105
+ public:
106
+ PrepareDllPath(const char *dllDirectory)
107
+ : setDllDirectory(0) {
108
+ logMsg("PrepareDllPath: %s", dllDirectory);
109
+ oldCurDir[0] = '\0';
110
+
111
+ // SetDllDirectory is present since XP SP1, so we have to load it dynamically
112
+ HINSTANCE hKernel32 = GetModuleHandle("kernel32");
113
+ if (!hKernel32) {
114
+ logErr(true, false, "Cannot load kernel32.");
115
+ return;
116
+ }
117
+
118
+ LPFNSDD setDllDirectory = (LPFNSDD)GetProcAddress(hKernel32, "SetDllDirectoryA");
119
+ if (setDllDirectory) {
120
+ setDllDirectory(dllDirectory);
121
+ } else {
122
+ logErr(true, false, "Cannot find SetDllDirectoryA");
123
+ }
124
+ GetCurrentDirectory(MAX_PATH, oldCurDir);
125
+ SetCurrentDirectory(dllDirectory);
126
+ }
127
+ ~PrepareDllPath() {
128
+ if (setDllDirectory) {
129
+ setDllDirectory(NULL);
130
+ }
131
+ if (oldCurDir[0]) {
132
+ SetCurrentDirectory(oldCurDir);
133
+ }
134
+ }
135
+ private:
136
+ typedef BOOL (WINAPI *LPFNSDD)(LPCTSTR lpPathname);
137
+ LPFNSDD setDllDirectory;
138
+ char oldCurDir[MAX_PATH];
139
+ };
140
+ };
141
+
142
+ #endif /* _JVMLAUNCHER_H */
143
+
@@ -1,3 +1,3 @@
1
- module JRubyLauncher
2
- VERSION = "1.1.1"
3
- end
1
+ module JRubyLauncher
2
+ VERSION = "1.1.2"
3
+ end
@@ -1,4 +1,4 @@
1
- class Gem::ConfigFile
2
- PLATFORM_DEFAULTS['install'] = '--no-rdoc --no-ri'
3
- PLATFORM_DEFAULTS['update'] = '--no-rdoc --no-ri'
4
- end
1
+ class Gem::ConfigFile
2
+ PLATFORM_DEFAULTS['install'] = '--no-rdoc --no-ri'
3
+ PLATFORM_DEFAULTS['update'] = '--no-rdoc --no-ri'
4
+ end
@@ -1,43 +1,43 @@
1
- #ifndef _NBEXECLOADER_H
2
- #define _NBEXECLOADER_H
3
-
4
- #include "utilsfuncs.h"
5
-
6
- #define HELP_MSG ""
7
-
8
- class NBExecLoader {
9
- typedef int (*StartPlatform)(int argc, char *argv[], const char *help, const char *name);
10
-
11
- public:
12
- NBExecLoader()
13
- : hLib(0) {
14
- }
15
- ~NBExecLoader() {
16
- if (hLib) {
17
- FreeLibrary(hLib);
18
- }
19
- }
20
- int start(const char *path, int argc, char *argv[], const char *name) {
21
- if (!hLib) {
22
- hLib = LoadLibrary(path);
23
- if (!hLib) {
24
- logErr(true, true, "Cannot load \"%s\".", path);
25
- return -1;
26
- }
27
- }
28
-
29
- StartPlatform startPlatform = (StartPlatform) GetProcAddress(hLib, "startPlatform");
30
- if (!startPlatform) {
31
- logErr(true, true, "Cannot start platform, failed to find startPlatform() in %s", path);
32
- return -1;
33
- }
34
- logMsg("Starting platform... \n\tBinary name is: %s\n", name);
35
- return startPlatform(argc, argv, HELP_MSG, name);
36
- }
37
-
38
- private:
39
- HMODULE hLib;
40
- };
41
-
42
- #endif /* _NBEXECLOADER_H */
43
-
1
+ #ifndef _NBEXECLOADER_H
2
+ #define _NBEXECLOADER_H
3
+
4
+ #include "utilsfuncs.h"
5
+
6
+ #define HELP_MSG ""
7
+
8
+ class NBExecLoader {
9
+ typedef int (*StartPlatform)(int argc, char *argv[], const char *help, const char *name);
10
+
11
+ public:
12
+ NBExecLoader()
13
+ : hLib(0) {
14
+ }
15
+ ~NBExecLoader() {
16
+ if (hLib) {
17
+ FreeLibrary(hLib);
18
+ }
19
+ }
20
+ int start(const char *path, int argc, char *argv[], const char *name) {
21
+ if (!hLib) {
22
+ hLib = LoadLibrary(path);
23
+ if (!hLib) {
24
+ logErr(true, true, "Cannot load \"%s\".", path);
25
+ return -1;
26
+ }
27
+ }
28
+
29
+ StartPlatform startPlatform = (StartPlatform) GetProcAddress(hLib, "startPlatform");
30
+ if (!startPlatform) {
31
+ logErr(true, true, "Cannot start platform, failed to find startPlatform() in %s", path);
32
+ return -1;
33
+ }
34
+ logMsg("Starting platform... \n\tBinary name is: %s\n", name);
35
+ return startPlatform(argc, argv, HELP_MSG, name);
36
+ }
37
+
38
+ private:
39
+ HMODULE hLib;
40
+ };
41
+
42
+ #endif /* _NBEXECLOADER_H */
43
+
data/ng.c CHANGED
@@ -1,730 +1,730 @@
1
- /*
2
-
3
- Copyright 2004-2012, Martian Software, Inc.
4
-
5
- Licensed under the Apache License, Version 2.0 (the "License");
6
- you may not use this file except in compliance with the License.
7
- You may obtain a copy of the License at
8
-
9
- http://www.apache.org/licenses/LICENSE-2.0
10
-
11
- Unless required by applicable law or agreed to in writing, software
12
- distributed under the License is distributed on an "AS IS" BASIS,
13
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
- See the License for the specific language governing permissions and
15
- limitations under the License.
16
-
17
- */
18
-
19
- /**
20
- * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
21
- * @author Pete Kirkham (Win32 port)
22
- */
23
-
24
- #ifdef WIN32
25
- #include <direct.h>
26
- #include <winsock2.h>
27
- #else
28
- #include <arpa/inet.h>
29
- #include <netdb.h>
30
- #include <netinet/in.h>
31
- #include <sys/socket.h>
32
- #include <sys/types.h>
33
- #endif
34
-
35
- #include <stdio.h>
36
- #include <stdlib.h>
37
- #include <string.h>
38
- #include <unistd.h>
39
- #include <fcntl.h>
40
-
41
- #define NAILGUN_VERSION "0.9.0"
42
-
43
- #define BUFSIZE (2048)
44
-
45
- #ifdef WIN32
46
- HANDLE NG_STDIN_FILENO;
47
- HANDLE NG_STDOUT_FILENO;
48
- HANDLE NG_STDERR_FILENO;
49
- #define FILE_SEPARATOR '\\'
50
- #define MSG_WAITALL 0
51
- #else
52
- #define NG_STDIN_FILENO STDIN_FILENO
53
- #define NG_STDOUT_FILENO STDOUT_FILENO
54
- #define NG_STDERR_FILENO STDERR_FILENO
55
- #define FILE_SEPARATOR '/'
56
- typedef int HANDLE;
57
- typedef unsigned int SOCKET;
58
- /* buffer used for reading an writing chunk data */
59
- char buf[BUFSIZE];
60
- #endif
61
-
62
- #ifndef MIN
63
- #define MIN(a,b) ((a<b)?(a):(b))
64
- #endif
65
-
66
- #ifdef WIN32
67
- #define NAILGUN_FILESEPARATOR "NAILGUN_FILESEPARATOR=\\"
68
- #define NAILGUN_PATHSEPARATOR "NAILGUN_PATHSEPARATOR=;"
69
- #else
70
- #define NAILGUN_FILESEPARATOR "NAILGUN_FILESEPARATOR=/"
71
- #define NAILGUN_PATHSEPARATOR "NAILGUN_PATHSEPARATOR=:"
72
- #endif
73
-
74
- #define NAILGUN_CLIENT_NAME_EXE "ng.exe"
75
-
76
- #define NAILGUN_PORT_DEFAULT "2113"
77
- #define NAILGUN_CLIENT_NAME "ng"
78
- #define CHUNK_HEADER_LEN (5)
79
-
80
- #define NAILGUN_SOCKET_FAILED (231)
81
- #define NAILGUN_CONNECT_FAILED (230)
82
- #define NAILGUN_UNEXPECTED_CHUNKTYPE (229)
83
- #define NAILGUN_EXCEPTION_ON_SERVER (228)
84
- #define NAILGUN_CONNECTION_BROKEN (227)
85
- #define NAILGUN_BAD_ARGUMENTS (226)
86
-
87
- #define CHUNKTYPE_STDIN '0'
88
- #define CHUNKTYPE_STDOUT '1'
89
- #define CHUNKTYPE_STDERR '2'
90
- #define CHUNKTYPE_STDIN_EOF '.'
91
- #define CHUNKTYPE_ARG 'A'
92
- #define CHUNKTYPE_LONGARG 'L'
93
- #define CHUNKTYPE_ENV 'E'
94
- #define CHUNKTYPE_DIR 'D'
95
- #define CHUNKTYPE_CMD 'C'
96
- #define CHUNKTYPE_EXIT 'X'
97
- #define CHUNKTYPE_STARTINPUT 'S'
98
-
99
- // jruby-launcher specific constant
100
- #define JRUBY_EXIT_EXCEPTION (1)
101
-
102
- /*
103
- the following is required to compile for hp-ux
104
- originally posted at http://jira.codehaus.org/browse/JRUBY-2346
105
- */
106
- #ifndef MSG_WAITALL
107
- #define MSG_WAITALL 0x40 /* wait for full request or error */
108
- #endif
109
-
110
- /* the socket connected to the nailgun server */
111
- int nailgunsocket = 0;
112
-
113
- /* buffer used for receiving and writing nail output chunks */
114
- char buf[BUFSIZE];
115
-
116
- /* track whether or not we've been told to send stdin to server */
117
- int startedInput = 0;
118
-
119
- /**
120
- * Clean up the application.
121
- */
122
- void cleanUpAndExit (int exitCode) {
123
-
124
-
125
- #ifdef WIN32
126
- CancelIo(STDIN_FILENO);
127
- WSACleanup();
128
- if (nailgunsocket) {
129
- closesocket(nailgunsocket);
130
- }
131
- #else
132
- close(nailgunsocket);
133
- #endif
134
-
135
- exit(exitCode);
136
- }
137
-
138
- #ifdef WIN32
139
- /**
140
- * Handles an error.
141
- * Shows the message for the latest error then exits.
142
- */
143
- void handleError () {
144
- LPVOID lpMsgBuf;
145
- int error = GetLastError();
146
-
147
- FormatMessage(
148
- FORMAT_MESSAGE_ALLOCATE_BUFFER |
149
- FORMAT_MESSAGE_FROM_SYSTEM |
150
- FORMAT_MESSAGE_IGNORE_INSERTS,
151
- NULL,
152
- error,
153
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
154
- (LPTSTR) &lpMsgBuf,
155
- 0,
156
- NULL);
157
-
158
- /* Display the string. */
159
- MessageBox( NULL, (LPCTSTR)lpMsgBuf, "Error", MB_OK | MB_ICONERROR );
160
-
161
- /* Free the buffer. */
162
- LocalFree( lpMsgBuf );
163
-
164
- cleanUpAndExit(error);
165
- }
166
- #endif
167
-
168
- /**
169
- * Writes everything in the specified buffer to the specified
170
- * socket handle.
171
- *
172
- * @param s the socket descriptor
173
- * @param buf the buffer containing the data to send
174
- * @param len the number of bytes to send. Also used to
175
- * return the number of bytes sent.
176
- * @return total bytes written or 0 if failure
177
- */
178
- int sendAll(SOCKET s, char *buf, int len) {
179
- int total = 0;
180
- int bytesleft = len;
181
- int n = 0;
182
-
183
- while(total < len) {
184
- n = send(s, buf+total, bytesleft, 0);
185
-
186
- if (n == -1) {
187
- break;
188
- }
189
-
190
- total += n;
191
- bytesleft -= n;
192
- }
193
-
194
- return n==-1 ? 0:total;
195
- }
196
-
197
- /**
198
- * Sends a chunk header noting the specified payload size and chunk type.
199
- *
200
- * @param size the payload size
201
- * @param chunkType the chunk type identifier
202
- */
203
- void sendHeader(unsigned int size, char chunkType) {
204
- /* buffer used for reading and writing chunk headers */
205
- char header[CHUNK_HEADER_LEN];
206
-
207
- header[0] = (size >> 24) & 0xff;
208
- header[1] = (size >> 16) & 0xff;
209
- header[2] = (size >> 8) & 0xff;
210
- header[3] = size & 0xff;
211
- header[4] = chunkType;
212
-
213
- sendAll(nailgunsocket, header, CHUNK_HEADER_LEN);
214
- }
215
-
216
- /**
217
- * Sends the contents of the specified file as a long argument (--nailgun-filearg)
218
- * This is sent as one or more chunks of type CHUNK_LONGARG. The end of the argument
219
- * is indicated by an empty chunk.
220
- *
221
- * @param filename the name of the file to send.
222
- * @return nonzero on failure
223
- */
224
- int sendFileArg(char *filename) {
225
- int i, f;
226
-
227
- if ((f = open(filename, O_RDONLY)) < 0) {
228
- perror("--nailgun-filearg");
229
- return 1;
230
- }
231
-
232
- i = read(f, buf, BUFSIZE);
233
- while (i > 0) {
234
- sendHeader(i, CHUNKTYPE_LONGARG);
235
- sendAll(nailgunsocket, buf, i);
236
- i = read(f, buf, BUFSIZE);
237
- }
238
- if (i < 0) {
239
- perror("--nailgun-filearg");
240
- return 1;
241
- }
242
- sendHeader(0, CHUNKTYPE_LONGARG);
243
-
244
- close(f);
245
- return 0;
246
- }
247
-
248
- /**
249
- * Sends a null-terminated string with the specified chunk type.
250
- *
251
- * @param chunkType the chunk type identifier
252
- * @param text the null-terminated string to send
253
- */
254
- void sendText(char chunkType, char *text) {
255
- int len = text ? strlen(text) : 0;
256
- sendHeader(len, chunkType);
257
- sendAll(nailgunsocket, text, len);
258
- }
259
-
260
- /**
261
- * Exits the client if the nailgun server ungracefully shut down the connection.
262
- */
263
- void handleSocketClose() {
264
- cleanUpAndExit(NAILGUN_CONNECTION_BROKEN);
265
- }
266
-
267
- /**
268
- * Receives len bytes from the nailgun socket and copies them to the specified file descriptor.
269
- * Used to route data to stdout or stderr on the client.
270
- *
271
- * @param destFD the destination file descriptor (stdout or stderr)
272
- * @param len the number of bytes to copy
273
- */
274
- void recvToFD(HANDLE destFD, char *buf, unsigned long len) {
275
- unsigned long bytesRead = 0;
276
- int bytesCopied;
277
-
278
- while (bytesRead < len) {
279
- unsigned long bytesRemaining = len - bytesRead;
280
- int bytesToRead = (BUFSIZE < bytesRemaining) ? BUFSIZE : bytesRemaining;
281
- int thisPass = 0;
282
-
283
- thisPass = recv(nailgunsocket, buf, bytesToRead, MSG_WAITALL);
284
- if (thisPass < bytesToRead) handleSocketClose();
285
-
286
-
287
- bytesRead += thisPass;
288
-
289
- bytesCopied = 0;
290
-
291
- while(bytesCopied < thisPass) {
292
- #ifdef WIN32
293
- DWORD thisWrite = 0;
294
-
295
- WriteFile(destFD, buf + bytesCopied, thisPass - bytesCopied,
296
- &thisWrite, NULL);
297
-
298
- if (thisWrite < 0) {
299
- break;
300
- }
301
-
302
- bytesCopied += thisWrite;
303
- #else
304
- bytesCopied += write(destFD, buf + bytesCopied, thisPass - bytesCopied);
305
- #endif
306
- }
307
- }
308
- }
309
-
310
-
311
- /**
312
- * Processes an exit chunk from the server. This is just a string
313
- * containing the exit code in decimal format. It should fit well
314
- * within our buffer, so assume that it does.
315
- *
316
- * @param len the current length of the buffer containing the exit code.
317
- */
318
- void processExit(char *buf, unsigned long len) {
319
- int exitcode;
320
- int bytesToRead = (BUFSIZE - 1 < len) ? BUFSIZE - 1 : len;
321
- int bytesRead = recv(nailgunsocket, buf, bytesToRead, MSG_WAITALL);
322
-
323
- if (bytesRead < 0) {
324
- handleSocketClose();
325
- }
326
-
327
- buf[bytesRead] = 0;
328
-
329
- exitcode = atoi(buf);
330
- if (exitcode != 0) {
331
- exitcode = JRUBY_EXIT_EXCEPTION;
332
- }
333
-
334
- cleanUpAndExit(exitcode);
335
- }
336
-
337
-
338
- /**
339
- * Sends len bytes from buf to the nailgun server in a stdin chunk.
340
- *
341
- * @param buf the bytes to send
342
- * @param len the number of bytes to send
343
- */
344
- void sendStdin(char *buf, unsigned int len) {
345
- sendHeader(len, CHUNKTYPE_STDIN);
346
- sendAll(nailgunsocket, buf, len);
347
- }
348
-
349
- /**
350
- * Sends a stdin-eof chunk to the nailgun server
351
- */
352
- void processEof() {
353
- sendHeader(0, CHUNKTYPE_STDIN_EOF);
354
- }
355
-
356
-
357
- #ifdef WIN32
358
- /**
359
- * Thread main for reading from stdin and sending
360
- */
361
- DWORD WINAPI processStdin (LPVOID lpParameter) {
362
- /* buffer used for reading and sending stdin chunks */
363
- char wbuf[BUFSIZE];
364
-
365
- for (;;) {
366
- DWORD numberOfBytes = 0;
367
-
368
- if (!ReadFile(NG_STDIN_FILENO, wbuf, BUFSIZE, &numberOfBytes, NULL)) {
369
- if (numberOfBytes != 0) {
370
- handleError();
371
- }
372
- }
373
-
374
- if (numberOfBytes > 0) {
375
- sendStdin(wbuf, numberOfBytes);
376
- } else {
377
- processEof();
378
- break;
379
- }
380
- }
381
-
382
- return 0;
383
- }
384
- #else
385
- /**
386
- * Reads from stdin and transmits it to the nailgun server in a stdin chunk.
387
- * Sends a stdin-eof chunk if necessary.
388
- *
389
- * @return zero if eof has been reached.
390
- */
391
- int processStdin() {
392
- int bytesread = read(STDIN_FILENO, buf, BUFSIZE);
393
- if (bytesread > 0) {
394
- sendStdin(buf, bytesread);
395
- } else if (bytesread == 0) {
396
- processEof();
397
- }
398
- return(bytesread);
399
- }
400
- #endif
401
-
402
- #ifdef WIN32
403
- /**
404
- * Initialise Windows sockets
405
- */
406
- void initSockets () {
407
- WSADATA win_socket_data; /* required to initialise winsock */
408
-
409
- WSAStartup(2, &win_socket_data);
410
- }
411
- #endif
412
-
413
- #ifdef WIN32
414
- /**
415
- * Initialise the asynchronous io.
416
- */
417
- void initIo () {
418
- /* create non-blocking console io */
419
- AllocConsole();
420
-
421
- NG_STDIN_FILENO = GetStdHandle(STD_INPUT_HANDLE);
422
- NG_STDOUT_FILENO = GetStdHandle(STD_OUTPUT_HANDLE);
423
- NG_STDERR_FILENO = GetStdHandle(STD_ERROR_HANDLE);
424
- }
425
- #endif
426
-
427
- #ifdef WIN32
428
- /**
429
- * Initialise the asynchronous io.
430
- */
431
- void winStartInput () {
432
- SECURITY_ATTRIBUTES securityAttributes;
433
- DWORD threadId = 0;
434
-
435
- securityAttributes.bInheritHandle = TRUE;
436
- securityAttributes.lpSecurityDescriptor = NULL;
437
- securityAttributes.nLength = 0;
438
-
439
- if (!CreateThread(&securityAttributes, 0, &processStdin, NULL, 0, &threadId)) {
440
- handleError();
441
- }
442
- }
443
- #endif
444
-
445
- /**
446
- * Processes data from the nailgun server.
447
- */
448
- void processnailgunstream() {
449
-
450
- /*for (;;) {*/
451
- int bytesRead = 0;
452
- unsigned long len;
453
- char chunkType;
454
-
455
- bytesRead = recv(nailgunsocket, buf, CHUNK_HEADER_LEN, MSG_WAITALL);
456
-
457
- if (bytesRead < CHUNK_HEADER_LEN) {
458
- handleSocketClose();
459
- }
460
-
461
- len = ((buf[0] << 24) & 0xff000000)
462
- | ((buf[1] << 16) & 0x00ff0000)
463
- | ((buf[2] << 8) & 0x0000ff00)
464
- | ((buf[3]) & 0x000000ff);
465
-
466
- chunkType = buf[4];
467
-
468
- switch(chunkType) {
469
- case CHUNKTYPE_STDOUT: recvToFD(NG_STDOUT_FILENO, buf, len);
470
- break;
471
- case CHUNKTYPE_STDERR: recvToFD(NG_STDERR_FILENO, buf, len);
472
- break;
473
- case CHUNKTYPE_EXIT: processExit(buf, len);
474
- break;
475
- case CHUNKTYPE_STARTINPUT:
476
- if (!startedInput) {
477
- #ifdef WIN32
478
- winStartInput();
479
- #endif
480
- startedInput = 1;
481
- }
482
- break;
483
- default: fprintf(stderr, "Unexpected chunk type %d ('%c')\n", chunkType, chunkType);
484
- cleanUpAndExit(NAILGUN_UNEXPECTED_CHUNKTYPE);
485
- }
486
- /*}*/
487
- }
488
-
489
- /**
490
- * Trims any path info from the beginning of argv[0] to determine
491
- * the name used to launch the client.
492
- *
493
- * @param s argv[0]
494
- */
495
- char *shortClientName(char *s) {
496
- char *result = strrchr(s, FILE_SEPARATOR);
497
- return ((result == NULL) ? s : result + 1);
498
- }
499
-
500
- /**
501
- * Returns true if the specified string is the name of the nailgun
502
- * client. The comparison is made case-insensitively for windows.
503
- *
504
- * @param s the program name to check
505
- */
506
- int isNailgunClientName(char *s) {
507
- #ifdef WIN32
508
- return (!strcasecmp(s, NAILGUN_CLIENT_NAME) ||
509
- !strcasecmp(s, NAILGUN_CLIENT_NAME_EXE));
510
- #else
511
- return(!(strcmp(s, NAILGUN_CLIENT_NAME)));
512
- #endif
513
- }
514
-
515
- /**
516
- * Displays usage info and bails
517
- */
518
- void usage(int exitcode) {
519
- fprintf(stderr, "NailGun v%s\n\n", NAILGUN_VERSION);
520
- fprintf(stderr, "Usage: ng class [--nailgun-options] [args]\n");
521
- fprintf(stderr, " (to execute a class)\n");
522
- fprintf(stderr, " or: ng alias [--nailgun-options] [args]\n");
523
- fprintf(stderr, " (to execute an aliased class)\n");
524
- fprintf(stderr, " or: alias [--nailgun-options] [args]\n");
525
- fprintf(stderr, " (to execute an aliased class, where \"alias\"\n");
526
- fprintf(stderr, " is both the alias for the class and a symbolic\n");
527
- fprintf(stderr, " link to the ng client)\n\n");
528
-
529
- fprintf(stderr, "where options include:\n");
530
- fprintf(stderr, " --nailgun-D<name>=<value> set/override a client environment variable\n");
531
- fprintf(stderr, " --nailgun-version print product version and exit\n");
532
- fprintf(stderr, " --nailgun-showversion print product version and continue\n");
533
- fprintf(stderr, " --nailgun-server to specify the address of the nailgun server\n");
534
- fprintf(stderr, " (default is NAILGUN_SERVER environment variable\n");
535
- fprintf(stderr, " if set, otherwise localhost)\n");
536
- fprintf(stderr, " --nailgun-port to specify the port of the nailgun server\n");
537
- fprintf(stderr, " (default is NAILGUN_PORT environment variable\n");
538
- fprintf(stderr, " if set, otherwise 2113)\n");
539
- fprintf(stderr, " --nailgun-filearg FILE places the entire contents of FILE into the\n");
540
- fprintf(stderr, " next argument, which is interpreted as a string\n");
541
- fprintf(stderr, " using the server's default character set. May be\n");
542
- fprintf(stderr, " specified more than once.\n");
543
- fprintf(stderr, " --nailgun-help print this message and exit\n");
544
-
545
- cleanUpAndExit(exitcode);
546
- }
547
-
548
- int nailgunClientMain(int argc, char *argv[], char *env[]) {
549
- int i;
550
- struct sockaddr_in server_addr;
551
- char *nailgun_server; /* server as specified by user */
552
- char *nailgun_port; /* port as specified by user */
553
- char *cwd;
554
- u_short port; /* port */
555
- struct hostent *hostinfo;
556
- char *cmd;
557
- int firstArgIndex; /* the first argument _to pass to the server_ */
558
-
559
- #ifndef WIN32
560
- fd_set readfds;
561
- int eof = 0;
562
- #endif
563
-
564
- #ifdef WIN32
565
- initSockets();
566
- #endif
567
-
568
- /* start with environment variable. default to localhost if not defined. */
569
- nailgun_server = getenv("NAILGUN_SERVER");
570
- if (nailgun_server == NULL) {
571
- nailgun_server = "127.0.0.1";
572
- }
573
-
574
- /* start with environment variable. default to normal nailgun port if not defined */
575
- nailgun_port = getenv("NAILGUN_PORT");
576
- if (nailgun_port == NULL) {
577
- nailgun_port = NAILGUN_PORT_DEFAULT;
578
- }
579
-
580
- /* look at the command used to launch this program. if it was "ng", then the actual
581
- command to issue to the server must be specified as another argument. if it
582
- wasn't ng, assume that the desired command name was symlinked to ng in the user's
583
- filesystem, and use the symlink name (without path info) as the command for the server. */
584
- cmd = shortClientName(argv[0]);
585
-
586
- if (isNailgunClientName(cmd)) {
587
- cmd = NULL;
588
- }
589
-
590
- /* if executing just the ng client with no arguments or -h|--help, then
591
- display usage and exit. Don't handle -h|--help if a command other than
592
- ng or ng.exe was used, since the appropriate nail should then handle
593
- --help. */
594
- if (cmd == NULL &&
595
- (argc == 1 ||
596
- (argc == 2 && strcmp("--help", argv[1]) == 0) ||
597
- (argc == 2 && strcmp("-h", argv[1]) == 0))) usage(0);
598
-
599
- firstArgIndex = 1;
600
-
601
- /* quite possibly the lamest commandline parsing ever.
602
- look for the two args we care about (--nailgun-server and
603
- --nailgun-port) and NULL them and their parameters after
604
- reading them if found. later, when we send args to the
605
- server, skip the null args. */
606
- for (i = 1; i < argc; ++i) {
607
- if (!strcmp("--nailgun-server", argv[i])) {
608
- if (i == argc - 1) usage(NAILGUN_BAD_ARGUMENTS);
609
- nailgun_server = argv[i + 1];
610
- argv[i] = argv[i + 1] = NULL;
611
- ++i;
612
- } else if(!strcmp("--nailgun-port", argv[i])) {
613
- if (i == argc - 1) usage(NAILGUN_BAD_ARGUMENTS);
614
- nailgun_port = argv[i + 1];
615
- argv[i] = argv[i + 1]= NULL;
616
- ++i;
617
- } else if (!strcmp("--nailgun-filearg", argv[i])) {
618
- /* just verify usage here. do the rest when sending args. */
619
- if (i == argc - 1) usage (NAILGUN_BAD_ARGUMENTS);
620
- } else if (!strcmp("--nailgun-version", argv[i])) {
621
- printf("NailGun client version %s\n", NAILGUN_VERSION);
622
- cleanUpAndExit(0);
623
- } else if (!strcmp("--nailgun-showversion", argv[i])) {
624
- printf("NailGun client version %s\n", NAILGUN_VERSION);
625
- argv[i] = NULL;
626
- } else if (!strcmp("--nailgun-help", argv[i])) {
627
- usage(0);
628
- } else if (cmd == NULL) {
629
- cmd = argv[i];
630
- firstArgIndex = i + 1;
631
- }
632
- }
633
-
634
- /* if there's no command, we should only display usage info
635
- if the version number was not displayed. */
636
- if (cmd == NULL) {
637
- usage(NAILGUN_BAD_ARGUMENTS);
638
- }
639
-
640
- /* jump through a series of connection hoops */
641
- hostinfo = gethostbyname(nailgun_server);
642
-
643
- if (hostinfo == NULL) {
644
- fprintf(stderr, "Unknown host: %s\n", nailgun_server);
645
- cleanUpAndExit(NAILGUN_CONNECT_FAILED);
646
- }
647
-
648
- port = atoi(nailgun_port);
649
-
650
- if ((nailgunsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
651
- perror("socket");
652
- cleanUpAndExit(NAILGUN_SOCKET_FAILED);
653
- }
654
-
655
- server_addr.sin_family = AF_INET;
656
- server_addr.sin_port = htons(port);
657
- server_addr.sin_addr = *(struct in_addr *) hostinfo->h_addr;
658
-
659
- memset(&(server_addr.sin_zero), '\0', 8);
660
-
661
- if (connect(nailgunsocket, (struct sockaddr *)&server_addr,
662
- sizeof(struct sockaddr)) == -1) {
663
- perror("connect");
664
- cleanUpAndExit(NAILGUN_CONNECT_FAILED);
665
- }
666
-
667
- /* ok, now we're connected. first send all of the command line
668
- arguments for the server, if any. remember that we may have
669
- marked some arguments NULL if we read them to specify the
670
- nailgun server and/or port */
671
- for(i = firstArgIndex; i < argc; ++i) {
672
- if (argv[i] != NULL) {
673
- if (!strcmp("--nailgun-filearg", argv[i])) {
674
- sendFileArg(argv[++i]);
675
- } else sendText(CHUNKTYPE_ARG, argv[i]);
676
- }
677
- }
678
-
679
- /* now send environment */
680
- sendText(CHUNKTYPE_ENV, NAILGUN_FILESEPARATOR);
681
- sendText(CHUNKTYPE_ENV, NAILGUN_PATHSEPARATOR);
682
- for(i = 0; env[i]; ++i) {
683
- sendText(CHUNKTYPE_ENV, env[i]);
684
- }
685
-
686
- /* now send the working directory */
687
- cwd = getcwd(NULL, 0);
688
- sendText(CHUNKTYPE_DIR, cwd);
689
- free(cwd);
690
-
691
- /* and finally send the command. this marks the point at which
692
- streams are linked between client and server. */
693
- sendText(CHUNKTYPE_CMD, cmd);
694
-
695
-
696
- /* initialise the std-* handles and the thread to send stdin to the server */
697
- #ifdef WIN32
698
- initIo();
699
- #endif
700
-
701
- /* stream forwarding loop */
702
- while(1) {
703
- #ifndef WIN32
704
- FD_ZERO(&readfds);
705
-
706
- /* don't select on stdin if we've already reached its end */
707
- if (startedInput && !eof) {
708
- FD_SET(NG_STDIN_FILENO, &readfds);
709
- }
710
-
711
- FD_SET(nailgunsocket, &readfds);
712
- if (select (nailgunsocket + 1, &readfds, NULL, NULL, NULL) == -1) {
713
- perror("select");
714
- }
715
-
716
- if (FD_ISSET(nailgunsocket, &readfds)) {
717
- #endif
718
- processnailgunstream();
719
- #ifndef WIN32
720
- } else if (FD_ISSET(NG_STDIN_FILENO, &readfds)) {
721
- if (!processStdin()) {
722
- FD_CLR(NG_STDIN_FILENO, &readfds);
723
- eof = 1;
724
- }
725
- }
726
- #endif
727
- }
728
-
729
- /* normal termination is triggered by the server, and so occurs in processExit(), above */
730
- }
1
+ /*
2
+
3
+ Copyright 2004-2012, Martian Software, Inc.
4
+
5
+ Licensed under the Apache License, Version 2.0 (the "License");
6
+ you may not use this file except in compliance with the License.
7
+ You may obtain a copy of the License at
8
+
9
+ http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ Unless required by applicable law or agreed to in writing, software
12
+ distributed under the License is distributed on an "AS IS" BASIS,
13
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ See the License for the specific language governing permissions and
15
+ limitations under the License.
16
+
17
+ */
18
+
19
+ /**
20
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
21
+ * @author Pete Kirkham (Win32 port)
22
+ */
23
+
24
+ #ifdef WIN32
25
+ #include <direct.h>
26
+ #include <winsock2.h>
27
+ #else
28
+ #include <arpa/inet.h>
29
+ #include <netdb.h>
30
+ #include <netinet/in.h>
31
+ #include <sys/socket.h>
32
+ #include <sys/types.h>
33
+ #endif
34
+
35
+ #include <stdio.h>
36
+ #include <stdlib.h>
37
+ #include <string.h>
38
+ #include <unistd.h>
39
+ #include <fcntl.h>
40
+
41
+ #define NAILGUN_VERSION "0.9.0"
42
+
43
+ #define BUFSIZE (2048)
44
+
45
+ #ifdef WIN32
46
+ HANDLE NG_STDIN_FILENO;
47
+ HANDLE NG_STDOUT_FILENO;
48
+ HANDLE NG_STDERR_FILENO;
49
+ #define FILE_SEPARATOR '\\'
50
+ #define MSG_WAITALL 0
51
+ #else
52
+ #define NG_STDIN_FILENO STDIN_FILENO
53
+ #define NG_STDOUT_FILENO STDOUT_FILENO
54
+ #define NG_STDERR_FILENO STDERR_FILENO
55
+ #define FILE_SEPARATOR '/'
56
+ typedef int HANDLE;
57
+ typedef unsigned int SOCKET;
58
+ /* buffer used for reading an writing chunk data */
59
+ char buf[BUFSIZE];
60
+ #endif
61
+
62
+ #ifndef MIN
63
+ #define MIN(a,b) ((a<b)?(a):(b))
64
+ #endif
65
+
66
+ #ifdef WIN32
67
+ #define NAILGUN_FILESEPARATOR "NAILGUN_FILESEPARATOR=\\"
68
+ #define NAILGUN_PATHSEPARATOR "NAILGUN_PATHSEPARATOR=;"
69
+ #else
70
+ #define NAILGUN_FILESEPARATOR "NAILGUN_FILESEPARATOR=/"
71
+ #define NAILGUN_PATHSEPARATOR "NAILGUN_PATHSEPARATOR=:"
72
+ #endif
73
+
74
+ #define NAILGUN_CLIENT_NAME_EXE "ng.exe"
75
+
76
+ #define NAILGUN_PORT_DEFAULT "2113"
77
+ #define NAILGUN_CLIENT_NAME "ng"
78
+ #define CHUNK_HEADER_LEN (5)
79
+
80
+ #define NAILGUN_SOCKET_FAILED (231)
81
+ #define NAILGUN_CONNECT_FAILED (230)
82
+ #define NAILGUN_UNEXPECTED_CHUNKTYPE (229)
83
+ #define NAILGUN_EXCEPTION_ON_SERVER (228)
84
+ #define NAILGUN_CONNECTION_BROKEN (227)
85
+ #define NAILGUN_BAD_ARGUMENTS (226)
86
+
87
+ #define CHUNKTYPE_STDIN '0'
88
+ #define CHUNKTYPE_STDOUT '1'
89
+ #define CHUNKTYPE_STDERR '2'
90
+ #define CHUNKTYPE_STDIN_EOF '.'
91
+ #define CHUNKTYPE_ARG 'A'
92
+ #define CHUNKTYPE_LONGARG 'L'
93
+ #define CHUNKTYPE_ENV 'E'
94
+ #define CHUNKTYPE_DIR 'D'
95
+ #define CHUNKTYPE_CMD 'C'
96
+ #define CHUNKTYPE_EXIT 'X'
97
+ #define CHUNKTYPE_STARTINPUT 'S'
98
+
99
+ // jruby-launcher specific constant
100
+ #define JRUBY_EXIT_EXCEPTION (1)
101
+
102
+ /*
103
+ the following is required to compile for hp-ux
104
+ originally posted at http://jira.codehaus.org/browse/JRUBY-2346
105
+ */
106
+ #ifndef MSG_WAITALL
107
+ #define MSG_WAITALL 0x40 /* wait for full request or error */
108
+ #endif
109
+
110
+ /* the socket connected to the nailgun server */
111
+ int nailgunsocket = 0;
112
+
113
+ /* buffer used for receiving and writing nail output chunks */
114
+ char buf[BUFSIZE];
115
+
116
+ /* track whether or not we've been told to send stdin to server */
117
+ int startedInput = 0;
118
+
119
+ /**
120
+ * Clean up the application.
121
+ */
122
+ void cleanUpAndExit (int exitCode) {
123
+
124
+
125
+ #ifdef WIN32
126
+ CancelIo(STDIN_FILENO);
127
+ WSACleanup();
128
+ if (nailgunsocket) {
129
+ closesocket(nailgunsocket);
130
+ }
131
+ #else
132
+ close(nailgunsocket);
133
+ #endif
134
+
135
+ exit(exitCode);
136
+ }
137
+
138
+ #ifdef WIN32
139
+ /**
140
+ * Handles an error.
141
+ * Shows the message for the latest error then exits.
142
+ */
143
+ void handleError () {
144
+ LPVOID lpMsgBuf;
145
+ int error = GetLastError();
146
+
147
+ FormatMessage(
148
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
149
+ FORMAT_MESSAGE_FROM_SYSTEM |
150
+ FORMAT_MESSAGE_IGNORE_INSERTS,
151
+ NULL,
152
+ error,
153
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
154
+ (LPTSTR) &lpMsgBuf,
155
+ 0,
156
+ NULL);
157
+
158
+ /* Display the string. */
159
+ MessageBox( NULL, (LPCTSTR)lpMsgBuf, "Error", MB_OK | MB_ICONERROR );
160
+
161
+ /* Free the buffer. */
162
+ LocalFree( lpMsgBuf );
163
+
164
+ cleanUpAndExit(error);
165
+ }
166
+ #endif
167
+
168
+ /**
169
+ * Writes everything in the specified buffer to the specified
170
+ * socket handle.
171
+ *
172
+ * @param s the socket descriptor
173
+ * @param buf the buffer containing the data to send
174
+ * @param len the number of bytes to send. Also used to
175
+ * return the number of bytes sent.
176
+ * @return total bytes written or 0 if failure
177
+ */
178
+ int sendAll(SOCKET s, char *buf, int len) {
179
+ int total = 0;
180
+ int bytesleft = len;
181
+ int n = 0;
182
+
183
+ while(total < len) {
184
+ n = send(s, buf+total, bytesleft, 0);
185
+
186
+ if (n == -1) {
187
+ break;
188
+ }
189
+
190
+ total += n;
191
+ bytesleft -= n;
192
+ }
193
+
194
+ return n==-1 ? 0:total;
195
+ }
196
+
197
+ /**
198
+ * Sends a chunk header noting the specified payload size and chunk type.
199
+ *
200
+ * @param size the payload size
201
+ * @param chunkType the chunk type identifier
202
+ */
203
+ void sendHeader(unsigned int size, char chunkType) {
204
+ /* buffer used for reading and writing chunk headers */
205
+ char header[CHUNK_HEADER_LEN];
206
+
207
+ header[0] = (size >> 24) & 0xff;
208
+ header[1] = (size >> 16) & 0xff;
209
+ header[2] = (size >> 8) & 0xff;
210
+ header[3] = size & 0xff;
211
+ header[4] = chunkType;
212
+
213
+ sendAll(nailgunsocket, header, CHUNK_HEADER_LEN);
214
+ }
215
+
216
+ /**
217
+ * Sends the contents of the specified file as a long argument (--nailgun-filearg)
218
+ * This is sent as one or more chunks of type CHUNK_LONGARG. The end of the argument
219
+ * is indicated by an empty chunk.
220
+ *
221
+ * @param filename the name of the file to send.
222
+ * @return nonzero on failure
223
+ */
224
+ int sendFileArg(char *filename) {
225
+ int i, f;
226
+
227
+ if ((f = open(filename, O_RDONLY)) < 0) {
228
+ perror("--nailgun-filearg");
229
+ return 1;
230
+ }
231
+
232
+ i = read(f, buf, BUFSIZE);
233
+ while (i > 0) {
234
+ sendHeader(i, CHUNKTYPE_LONGARG);
235
+ sendAll(nailgunsocket, buf, i);
236
+ i = read(f, buf, BUFSIZE);
237
+ }
238
+ if (i < 0) {
239
+ perror("--nailgun-filearg");
240
+ return 1;
241
+ }
242
+ sendHeader(0, CHUNKTYPE_LONGARG);
243
+
244
+ close(f);
245
+ return 0;
246
+ }
247
+
248
+ /**
249
+ * Sends a null-terminated string with the specified chunk type.
250
+ *
251
+ * @param chunkType the chunk type identifier
252
+ * @param text the null-terminated string to send
253
+ */
254
+ void sendText(char chunkType, char *text) {
255
+ int len = text ? strlen(text) : 0;
256
+ sendHeader(len, chunkType);
257
+ sendAll(nailgunsocket, text, len);
258
+ }
259
+
260
+ /**
261
+ * Exits the client if the nailgun server ungracefully shut down the connection.
262
+ */
263
+ void handleSocketClose() {
264
+ cleanUpAndExit(NAILGUN_CONNECTION_BROKEN);
265
+ }
266
+
267
+ /**
268
+ * Receives len bytes from the nailgun socket and copies them to the specified file descriptor.
269
+ * Used to route data to stdout or stderr on the client.
270
+ *
271
+ * @param destFD the destination file descriptor (stdout or stderr)
272
+ * @param len the number of bytes to copy
273
+ */
274
+ void recvToFD(HANDLE destFD, char *buf, unsigned long len) {
275
+ unsigned long bytesRead = 0;
276
+ int bytesCopied;
277
+
278
+ while (bytesRead < len) {
279
+ unsigned long bytesRemaining = len - bytesRead;
280
+ int bytesToRead = (BUFSIZE < bytesRemaining) ? BUFSIZE : bytesRemaining;
281
+ int thisPass = 0;
282
+
283
+ thisPass = recv(nailgunsocket, buf, bytesToRead, MSG_WAITALL);
284
+ if (thisPass < bytesToRead) handleSocketClose();
285
+
286
+
287
+ bytesRead += thisPass;
288
+
289
+ bytesCopied = 0;
290
+
291
+ while(bytesCopied < thisPass) {
292
+ #ifdef WIN32
293
+ DWORD thisWrite = 0;
294
+
295
+ WriteFile(destFD, buf + bytesCopied, thisPass - bytesCopied,
296
+ &thisWrite, NULL);
297
+
298
+ if (thisWrite < 0) {
299
+ break;
300
+ }
301
+
302
+ bytesCopied += thisWrite;
303
+ #else
304
+ bytesCopied += write(destFD, buf + bytesCopied, thisPass - bytesCopied);
305
+ #endif
306
+ }
307
+ }
308
+ }
309
+
310
+
311
+ /**
312
+ * Processes an exit chunk from the server. This is just a string
313
+ * containing the exit code in decimal format. It should fit well
314
+ * within our buffer, so assume that it does.
315
+ *
316
+ * @param len the current length of the buffer containing the exit code.
317
+ */
318
+ void processExit(char *buf, unsigned long len) {
319
+ int exitcode;
320
+ int bytesToRead = (BUFSIZE - 1 < len) ? BUFSIZE - 1 : len;
321
+ int bytesRead = recv(nailgunsocket, buf, bytesToRead, MSG_WAITALL);
322
+
323
+ if (bytesRead < 0) {
324
+ handleSocketClose();
325
+ }
326
+
327
+ buf[bytesRead] = 0;
328
+
329
+ exitcode = atoi(buf);
330
+ if (exitcode != 0) {
331
+ exitcode = JRUBY_EXIT_EXCEPTION;
332
+ }
333
+
334
+ cleanUpAndExit(exitcode);
335
+ }
336
+
337
+
338
+ /**
339
+ * Sends len bytes from buf to the nailgun server in a stdin chunk.
340
+ *
341
+ * @param buf the bytes to send
342
+ * @param len the number of bytes to send
343
+ */
344
+ void sendStdin(char *buf, unsigned int len) {
345
+ sendHeader(len, CHUNKTYPE_STDIN);
346
+ sendAll(nailgunsocket, buf, len);
347
+ }
348
+
349
+ /**
350
+ * Sends a stdin-eof chunk to the nailgun server
351
+ */
352
+ void processEof() {
353
+ sendHeader(0, CHUNKTYPE_STDIN_EOF);
354
+ }
355
+
356
+
357
+ #ifdef WIN32
358
+ /**
359
+ * Thread main for reading from stdin and sending
360
+ */
361
+ DWORD WINAPI processStdin (LPVOID lpParameter) {
362
+ /* buffer used for reading and sending stdin chunks */
363
+ char wbuf[BUFSIZE];
364
+
365
+ for (;;) {
366
+ DWORD numberOfBytes = 0;
367
+
368
+ if (!ReadFile(NG_STDIN_FILENO, wbuf, BUFSIZE, &numberOfBytes, NULL)) {
369
+ if (numberOfBytes != 0) {
370
+ handleError();
371
+ }
372
+ }
373
+
374
+ if (numberOfBytes > 0) {
375
+ sendStdin(wbuf, numberOfBytes);
376
+ } else {
377
+ processEof();
378
+ break;
379
+ }
380
+ }
381
+
382
+ return 0;
383
+ }
384
+ #else
385
+ /**
386
+ * Reads from stdin and transmits it to the nailgun server in a stdin chunk.
387
+ * Sends a stdin-eof chunk if necessary.
388
+ *
389
+ * @return zero if eof has been reached.
390
+ */
391
+ int processStdin() {
392
+ int bytesread = read(STDIN_FILENO, buf, BUFSIZE);
393
+ if (bytesread > 0) {
394
+ sendStdin(buf, bytesread);
395
+ } else if (bytesread == 0) {
396
+ processEof();
397
+ }
398
+ return(bytesread);
399
+ }
400
+ #endif
401
+
402
+ #ifdef WIN32
403
+ /**
404
+ * Initialise Windows sockets
405
+ */
406
+ void initSockets () {
407
+ WSADATA win_socket_data; /* required to initialise winsock */
408
+
409
+ WSAStartup(2, &win_socket_data);
410
+ }
411
+ #endif
412
+
413
+ #ifdef WIN32
414
+ /**
415
+ * Initialise the asynchronous io.
416
+ */
417
+ void initIo () {
418
+ /* create non-blocking console io */
419
+ AllocConsole();
420
+
421
+ NG_STDIN_FILENO = GetStdHandle(STD_INPUT_HANDLE);
422
+ NG_STDOUT_FILENO = GetStdHandle(STD_OUTPUT_HANDLE);
423
+ NG_STDERR_FILENO = GetStdHandle(STD_ERROR_HANDLE);
424
+ }
425
+ #endif
426
+
427
+ #ifdef WIN32
428
+ /**
429
+ * Initialise the asynchronous io.
430
+ */
431
+ void winStartInput () {
432
+ SECURITY_ATTRIBUTES securityAttributes;
433
+ DWORD threadId = 0;
434
+
435
+ securityAttributes.bInheritHandle = TRUE;
436
+ securityAttributes.lpSecurityDescriptor = NULL;
437
+ securityAttributes.nLength = 0;
438
+
439
+ if (!CreateThread(&securityAttributes, 0, &processStdin, NULL, 0, &threadId)) {
440
+ handleError();
441
+ }
442
+ }
443
+ #endif
444
+
445
+ /**
446
+ * Processes data from the nailgun server.
447
+ */
448
+ void processnailgunstream() {
449
+
450
+ /*for (;;) {*/
451
+ int bytesRead = 0;
452
+ unsigned long len;
453
+ char chunkType;
454
+
455
+ bytesRead = recv(nailgunsocket, buf, CHUNK_HEADER_LEN, MSG_WAITALL);
456
+
457
+ if (bytesRead < CHUNK_HEADER_LEN) {
458
+ handleSocketClose();
459
+ }
460
+
461
+ len = ((buf[0] << 24) & 0xff000000)
462
+ | ((buf[1] << 16) & 0x00ff0000)
463
+ | ((buf[2] << 8) & 0x0000ff00)
464
+ | ((buf[3]) & 0x000000ff);
465
+
466
+ chunkType = buf[4];
467
+
468
+ switch(chunkType) {
469
+ case CHUNKTYPE_STDOUT: recvToFD(NG_STDOUT_FILENO, buf, len);
470
+ break;
471
+ case CHUNKTYPE_STDERR: recvToFD(NG_STDERR_FILENO, buf, len);
472
+ break;
473
+ case CHUNKTYPE_EXIT: processExit(buf, len);
474
+ break;
475
+ case CHUNKTYPE_STARTINPUT:
476
+ if (!startedInput) {
477
+ #ifdef WIN32
478
+ winStartInput();
479
+ #endif
480
+ startedInput = 1;
481
+ }
482
+ break;
483
+ default: fprintf(stderr, "Unexpected chunk type %d ('%c')\n", chunkType, chunkType);
484
+ cleanUpAndExit(NAILGUN_UNEXPECTED_CHUNKTYPE);
485
+ }
486
+ /*}*/
487
+ }
488
+
489
+ /**
490
+ * Trims any path info from the beginning of argv[0] to determine
491
+ * the name used to launch the client.
492
+ *
493
+ * @param s argv[0]
494
+ */
495
+ char *shortClientName(char *s) {
496
+ char *result = strrchr(s, FILE_SEPARATOR);
497
+ return ((result == NULL) ? s : result + 1);
498
+ }
499
+
500
+ /**
501
+ * Returns true if the specified string is the name of the nailgun
502
+ * client. The comparison is made case-insensitively for windows.
503
+ *
504
+ * @param s the program name to check
505
+ */
506
+ int isNailgunClientName(char *s) {
507
+ #ifdef WIN32
508
+ return (!strcasecmp(s, NAILGUN_CLIENT_NAME) ||
509
+ !strcasecmp(s, NAILGUN_CLIENT_NAME_EXE));
510
+ #else
511
+ return(!(strcmp(s, NAILGUN_CLIENT_NAME)));
512
+ #endif
513
+ }
514
+
515
+ /**
516
+ * Displays usage info and bails
517
+ */
518
+ void usage(int exitcode) {
519
+ fprintf(stderr, "NailGun v%s\n\n", NAILGUN_VERSION);
520
+ fprintf(stderr, "Usage: ng class [--nailgun-options] [args]\n");
521
+ fprintf(stderr, " (to execute a class)\n");
522
+ fprintf(stderr, " or: ng alias [--nailgun-options] [args]\n");
523
+ fprintf(stderr, " (to execute an aliased class)\n");
524
+ fprintf(stderr, " or: alias [--nailgun-options] [args]\n");
525
+ fprintf(stderr, " (to execute an aliased class, where \"alias\"\n");
526
+ fprintf(stderr, " is both the alias for the class and a symbolic\n");
527
+ fprintf(stderr, " link to the ng client)\n\n");
528
+
529
+ fprintf(stderr, "where options include:\n");
530
+ fprintf(stderr, " --nailgun-D<name>=<value> set/override a client environment variable\n");
531
+ fprintf(stderr, " --nailgun-version print product version and exit\n");
532
+ fprintf(stderr, " --nailgun-showversion print product version and continue\n");
533
+ fprintf(stderr, " --nailgun-server to specify the address of the nailgun server\n");
534
+ fprintf(stderr, " (default is NAILGUN_SERVER environment variable\n");
535
+ fprintf(stderr, " if set, otherwise localhost)\n");
536
+ fprintf(stderr, " --nailgun-port to specify the port of the nailgun server\n");
537
+ fprintf(stderr, " (default is NAILGUN_PORT environment variable\n");
538
+ fprintf(stderr, " if set, otherwise 2113)\n");
539
+ fprintf(stderr, " --nailgun-filearg FILE places the entire contents of FILE into the\n");
540
+ fprintf(stderr, " next argument, which is interpreted as a string\n");
541
+ fprintf(stderr, " using the server's default character set. May be\n");
542
+ fprintf(stderr, " specified more than once.\n");
543
+ fprintf(stderr, " --nailgun-help print this message and exit\n");
544
+
545
+ cleanUpAndExit(exitcode);
546
+ }
547
+
548
+ int nailgunClientMain(int argc, char *argv[], char *env[]) {
549
+ int i;
550
+ struct sockaddr_in server_addr;
551
+ char *nailgun_server; /* server as specified by user */
552
+ char *nailgun_port; /* port as specified by user */
553
+ char *cwd;
554
+ u_short port; /* port */
555
+ struct hostent *hostinfo;
556
+ char *cmd;
557
+ int firstArgIndex; /* the first argument _to pass to the server_ */
558
+
559
+ #ifndef WIN32
560
+ fd_set readfds;
561
+ int eof = 0;
562
+ #endif
563
+
564
+ #ifdef WIN32
565
+ initSockets();
566
+ #endif
567
+
568
+ /* start with environment variable. default to localhost if not defined. */
569
+ nailgun_server = getenv("NAILGUN_SERVER");
570
+ if (nailgun_server == NULL) {
571
+ nailgun_server = "127.0.0.1";
572
+ }
573
+
574
+ /* start with environment variable. default to normal nailgun port if not defined */
575
+ nailgun_port = getenv("NAILGUN_PORT");
576
+ if (nailgun_port == NULL) {
577
+ nailgun_port = NAILGUN_PORT_DEFAULT;
578
+ }
579
+
580
+ /* look at the command used to launch this program. if it was "ng", then the actual
581
+ command to issue to the server must be specified as another argument. if it
582
+ wasn't ng, assume that the desired command name was symlinked to ng in the user's
583
+ filesystem, and use the symlink name (without path info) as the command for the server. */
584
+ cmd = shortClientName(argv[0]);
585
+
586
+ if (isNailgunClientName(cmd)) {
587
+ cmd = NULL;
588
+ }
589
+
590
+ /* if executing just the ng client with no arguments or -h|--help, then
591
+ display usage and exit. Don't handle -h|--help if a command other than
592
+ ng or ng.exe was used, since the appropriate nail should then handle
593
+ --help. */
594
+ if (cmd == NULL &&
595
+ (argc == 1 ||
596
+ (argc == 2 && strcmp("--help", argv[1]) == 0) ||
597
+ (argc == 2 && strcmp("-h", argv[1]) == 0))) usage(0);
598
+
599
+ firstArgIndex = 1;
600
+
601
+ /* quite possibly the lamest commandline parsing ever.
602
+ look for the two args we care about (--nailgun-server and
603
+ --nailgun-port) and NULL them and their parameters after
604
+ reading them if found. later, when we send args to the
605
+ server, skip the null args. */
606
+ for (i = 1; i < argc; ++i) {
607
+ if (!strcmp("--nailgun-server", argv[i])) {
608
+ if (i == argc - 1) usage(NAILGUN_BAD_ARGUMENTS);
609
+ nailgun_server = argv[i + 1];
610
+ argv[i] = argv[i + 1] = NULL;
611
+ ++i;
612
+ } else if(!strcmp("--nailgun-port", argv[i])) {
613
+ if (i == argc - 1) usage(NAILGUN_BAD_ARGUMENTS);
614
+ nailgun_port = argv[i + 1];
615
+ argv[i] = argv[i + 1]= NULL;
616
+ ++i;
617
+ } else if (!strcmp("--nailgun-filearg", argv[i])) {
618
+ /* just verify usage here. do the rest when sending args. */
619
+ if (i == argc - 1) usage (NAILGUN_BAD_ARGUMENTS);
620
+ } else if (!strcmp("--nailgun-version", argv[i])) {
621
+ printf("NailGun client version %s\n", NAILGUN_VERSION);
622
+ cleanUpAndExit(0);
623
+ } else if (!strcmp("--nailgun-showversion", argv[i])) {
624
+ printf("NailGun client version %s\n", NAILGUN_VERSION);
625
+ argv[i] = NULL;
626
+ } else if (!strcmp("--nailgun-help", argv[i])) {
627
+ usage(0);
628
+ } else if (cmd == NULL) {
629
+ cmd = argv[i];
630
+ firstArgIndex = i + 1;
631
+ }
632
+ }
633
+
634
+ /* if there's no command, we should only display usage info
635
+ if the version number was not displayed. */
636
+ if (cmd == NULL) {
637
+ usage(NAILGUN_BAD_ARGUMENTS);
638
+ }
639
+
640
+ /* jump through a series of connection hoops */
641
+ hostinfo = gethostbyname(nailgun_server);
642
+
643
+ if (hostinfo == NULL) {
644
+ fprintf(stderr, "Unknown host: %s\n", nailgun_server);
645
+ cleanUpAndExit(NAILGUN_CONNECT_FAILED);
646
+ }
647
+
648
+ port = atoi(nailgun_port);
649
+
650
+ if ((nailgunsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
651
+ perror("socket");
652
+ cleanUpAndExit(NAILGUN_SOCKET_FAILED);
653
+ }
654
+
655
+ server_addr.sin_family = AF_INET;
656
+ server_addr.sin_port = htons(port);
657
+ server_addr.sin_addr = *(struct in_addr *) hostinfo->h_addr;
658
+
659
+ memset(&(server_addr.sin_zero), '\0', 8);
660
+
661
+ if (connect(nailgunsocket, (struct sockaddr *)&server_addr,
662
+ sizeof(struct sockaddr)) == -1) {
663
+ perror("connect");
664
+ cleanUpAndExit(NAILGUN_CONNECT_FAILED);
665
+ }
666
+
667
+ /* ok, now we're connected. first send all of the command line
668
+ arguments for the server, if any. remember that we may have
669
+ marked some arguments NULL if we read them to specify the
670
+ nailgun server and/or port */
671
+ for(i = firstArgIndex; i < argc; ++i) {
672
+ if (argv[i] != NULL) {
673
+ if (!strcmp("--nailgun-filearg", argv[i])) {
674
+ sendFileArg(argv[++i]);
675
+ } else sendText(CHUNKTYPE_ARG, argv[i]);
676
+ }
677
+ }
678
+
679
+ /* now send environment */
680
+ sendText(CHUNKTYPE_ENV, NAILGUN_FILESEPARATOR);
681
+ sendText(CHUNKTYPE_ENV, NAILGUN_PATHSEPARATOR);
682
+ for(i = 0; env[i]; ++i) {
683
+ sendText(CHUNKTYPE_ENV, env[i]);
684
+ }
685
+
686
+ /* now send the working directory */
687
+ cwd = getcwd(NULL, 0);
688
+ sendText(CHUNKTYPE_DIR, cwd);
689
+ free(cwd);
690
+
691
+ /* and finally send the command. this marks the point at which
692
+ streams are linked between client and server. */
693
+ sendText(CHUNKTYPE_CMD, cmd);
694
+
695
+
696
+ /* initialise the std-* handles and the thread to send stdin to the server */
697
+ #ifdef WIN32
698
+ initIo();
699
+ #endif
700
+
701
+ /* stream forwarding loop */
702
+ while(1) {
703
+ #ifndef WIN32
704
+ FD_ZERO(&readfds);
705
+
706
+ /* don't select on stdin if we've already reached its end */
707
+ if (startedInput && !eof) {
708
+ FD_SET(NG_STDIN_FILENO, &readfds);
709
+ }
710
+
711
+ FD_SET(nailgunsocket, &readfds);
712
+ if (select (nailgunsocket + 1, &readfds, NULL, NULL, NULL) == -1) {
713
+ perror("select");
714
+ }
715
+
716
+ if (FD_ISSET(nailgunsocket, &readfds)) {
717
+ #endif
718
+ processnailgunstream();
719
+ #ifndef WIN32
720
+ } else if (FD_ISSET(NG_STDIN_FILENO, &readfds)) {
721
+ if (!processStdin()) {
722
+ FD_CLR(NG_STDIN_FILENO, &readfds);
723
+ eof = 1;
724
+ }
725
+ }
726
+ #endif
727
+ }
728
+
729
+ /* normal termination is triggered by the server, and so occurs in processExit(), above */
730
+ }