1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package com.sun.akuma;
25
26 import com.sun.jna.StringArray;
27
28 import java.io.FileDescriptor;
29 import java.lang.reflect.Constructor;
30 import java.lang.reflect.Field;
31 import java.lang.reflect.InvocationTargetException;
32 import java.lang.reflect.Method;
33 import java.net.InetSocketAddress;
34 import java.net.ServerSocket;
35 import java.net.SocketImpl;
36 import java.util.logging.Logger;
37 import java.util.List;
38 import java.util.Collections;
39 import java.util.Arrays;
40
41 import static com.sun.akuma.CLibrary.LIBC;
42 import sun.misc.Signal;
43 import sun.misc.SignalHandler;
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71 public abstract class NetworkServer extends Daemon {
72
73
74
75 protected final List<String> arguments;
76
77 protected NetworkServer(String[] args) {
78 this.arguments = Collections.unmodifiableList(Arrays.asList(args));
79 }
80
81
82
83
84 public void run() throws Exception {
85 String mode = System.getProperty(MODE_PROPERTY);
86 if("worker".equals(mode)) {
87
88 worker();
89 } else {
90
91 if(isDaemonized()) {
92
93 init();
94 } else {
95
96 if(shouldBeDaemonized()) {
97
98 daemonize();
99 System.exit(0);
100 }
101 }
102
103 frontend();
104 }
105 }
106
107
108
109
110 protected boolean shouldBeDaemonized() {
111 return !arguments.isEmpty() && arguments.get(0).equals("daemonize");
112 }
113
114
115
116
117 protected void frontend() throws Exception {
118 ServerSocket ss = createServerSocket();
119 int fdn = getUnixFileDescriptor(ss);
120
121 LOGGER.fine("Listening to port "+ss.getLocalPort()+" (fd="+fdn+")");
122
123
124 JavaVMArguments forkArgs = JavaVMArguments.current();
125 forkArgs.setSystemProperty(NetworkServer.class.getName()+".port",String.valueOf(fdn));
126
127 forkWorkers(forkArgs);
128 }
129
130
131
132
133
134
135
136 protected abstract void forkWorkers(JavaVMArguments args) throws Exception;
137
138
139
140
141
142
143 protected void forkWorkerThreads(JavaVMArguments arguments, int n) throws Exception {
144 String exe = Daemon.getCurrentExecutable();
145 arguments.setSystemProperty(MODE_PROPERTY,"worker");
146 LOGGER.fine("Forking worker: "+arguments);
147 StringArray sa = arguments.toStringArray();
148
149
150 for( int i=0; i< n; i++ ) {
151 int r = LIBC.fork();
152 if(r<0) {
153 LIBC.perror("forking a worker process failed");
154 System.exit(-1);
155 }
156 if(r==0) {
157
158 LIBC.execv(exe,sa);
159 System.err.println("exec failed");
160 LIBC.perror("initial exec failed");
161 System.exit(-1);
162 }
163 }
164
165
166 Signal.handle(new Signal("TERM"),
167 new SignalHandler() {
168 public void handle(Signal sig) {
169 LIBC.kill(0,SIGTERM);
170 System.exit(-1);
171 }
172 });
173
174
175 Object o = new Object();
176 synchronized (o) {
177 o.wait();
178 }
179 }
180
181
182
183
184
185 protected abstract ServerSocket createServerSocket() throws Exception;
186
187
188
189
190 private int getUnixFileDescriptor(ServerSocket ss) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
191 Field $impl = ss.getClass().getDeclaredField("impl");
192 $impl.setAccessible(true);
193 SocketImpl socketImpl = (SocketImpl)$impl.get(ss);
194 Method $getFileDescriptor = SocketImpl.class.getDeclaredMethod("getFileDescriptor");
195 $getFileDescriptor.setAccessible(true);
196 FileDescriptor fd = (FileDescriptor) $getFileDescriptor.invoke(socketImpl);
197 Field $fd = fd.getClass().getDeclaredField("fd");
198 $fd.setAccessible(true);
199 return (Integer)$fd.get(fd);
200 }
201
202 protected void worker() throws Exception {
203 String port = System.getProperty(NetworkServer.class.getName() + ".port");
204 worker(recreateServerSocket(Integer.parseInt(port)));
205 }
206
207
208
209
210
211
212
213 protected abstract void worker(ServerSocket ss) throws Exception;
214
215
216
217
218 private ServerSocket recreateServerSocket(int fdn) throws Exception {
219
220 FileDescriptor fd = new FileDescriptor();
221 Field $fd = FileDescriptor.class.getDeclaredField("fd");
222 $fd.setAccessible(true);
223 $fd.set(fd,fdn);
224
225
226 Class $PlainSocketImpl = Class.forName("java.net.PlainSocketImpl");
227 Constructor $init = $PlainSocketImpl.getDeclaredConstructor(FileDescriptor.class);
228 $init.setAccessible(true);
229 SocketImpl socketImpl = (SocketImpl)$init.newInstance(fd);
230
231
232 ServerSocket ss = new ServerSocket();
233 ss.bind(new InetSocketAddress(0));
234 Field $impl = ServerSocket.class.getDeclaredField("impl");
235 $impl.setAccessible(true);
236 $impl.set(ss,socketImpl);
237 return ss;
238 }
239
240 private static final Logger LOGGER = Logger.getLogger(NetworkServer.class.getName());
241 private static final int SIGTERM = 15;
242 private static final String MODE_PROPERTY = NetworkServer.class.getName() + ".mode";
243 }