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.
- checksums.yaml +4 -4
- data/COPYING +826 -826
- data/Makefile +122 -122
- data/README.md +64 -64
- data/Rakefile +51 -51
- data/argnames.h +35 -35
- data/argparser.cpp +668 -667
- data/argparser.h +78 -78
- data/extconf.rb +7 -7
- data/inc/Makefile-conf.mk +76 -76
- data/inc/Makefile-impl.mk +107 -107
- data/inc/Makefile-rules.mk +40 -40
- data/jruby.cpp +77 -77
- data/jrubyexe.cpp +84 -84
- data/jvmlauncher.cpp +459 -454
- data/jvmlauncher.h +143 -143
- data/lib/jruby-launcher.rb +3 -3
- data/lib/rubygems/defaults/jruby_native.rb +4 -4
- data/nbexecloader.h +43 -43
- data/ng.c +730 -730
- data/platformlauncher.cpp +252 -252
- data/platformlauncher.h +73 -73
- data/rb_w32_cmdvector.h +287 -287
- data/resources/jruby.rc +25 -25
- data/spec/launcher_spec.rb +262 -258
- data/spec/spec_helper.rb +81 -81
- data/strlcpy.c +72 -72
- data/unixlauncher.cpp +100 -100
- data/unixlauncher.h +22 -22
- data/utilsfuncs.cpp +279 -279
- data/utilsfuncs.h +85 -85
- data/utilsfuncswin.cpp +229 -229
- data/version.h +1 -1
- metadata +3 -3
data/jvmlauncher.h
CHANGED
@@ -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
|
+
|
data/lib/jruby-launcher.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
module JRubyLauncher
|
2
|
-
VERSION = "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
|
data/nbexecloader.h
CHANGED
@@ -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
|
+
}
|