]> git.proxmox.com Git - ceph.git/blob - ceph/src/civetweb/src/third_party/duktape-1.5.2/examples/debug-trans-socket/duk_trans_socket_windows.c
e92ac2660943939544604a6ae61a3c3fabe1176c
[ceph.git] / ceph / src / civetweb / src / third_party / duktape-1.5.2 / examples / debug-trans-socket / duk_trans_socket_windows.c
1 /*
2 * Example debug transport using a Windows TCP socket
3 *
4 * Provides a TCP server socket which a debug client can connect to.
5 * After that data is just passed through.
6 *
7 * https://msdn.microsoft.com/en-us/library/windows/desktop/ms737593(v=vs.85).aspx
8 *
9 * Compiling 'duk' with debugger support using MSVC (Visual Studio):
10 *
11 * > cl /W3 /O2 /Feduk.exe
12 * /DDUK_OPT_DEBUGGER_SUPPORT /DDUK_OPT_INTERRUPT_COUNTER
13 * /DDUK_CMDLINE_DEBUGGER_SUPPORT
14 * /Iexamples\debug-trans-socket /Isrc
15 * examples\cmdline\duk_cmdline.c
16 * examples\debug-trans-socket\duk_trans_socket_windows.c
17 * src\duktape.c
18 *
19 * With MinGW:
20 *
21 * $ gcc -oduk.exe -Wall -O2 \
22 * -DDUK_OPT_DEBUGGER_SUPPORT -DDUK_OPT_INTERRUPT_COUNTER \
23 * -DDUK_CMDLINE_DEBUGGER_SUPPORT \
24 * -Iexamples/debug-trans-socket -Isrc \
25 * examples/cmdline/duk_cmdline.c \
26 * examples/debug-trans-socket/duk_trans_socket_windows.c \
27 * src/duktape.c -lm -lws2_32
28 */
29
30 #undef UNICODE
31 #if !defined(WIN32_LEAN_AND_MEAN)
32 #define WIN32_LEAN_AND_MEAN
33 #endif
34
35 /* MinGW workaround for missing getaddrinfo() etc:
36 * http://programmingrants.blogspot.fi/2009/09/tips-on-undefined-reference-to.html
37 */
38 #if defined(__MINGW32__) || defined(__MINGW64__)
39 #if !defined(_WIN32_WINNT)
40 #define _WIN32_WINNT 0x0501
41 #endif
42 #endif
43
44 #include <windows.h>
45 #include <winsock2.h>
46 #include <ws2tcpip.h>
47 #include <stdio.h>
48 #include <string.h>
49 #include "duktape.h"
50
51 #if defined(_MSC_VER)
52 #pragma comment (lib, "Ws2_32.lib")
53 #endif
54
55 #if !defined(DUK_DEBUG_PORT)
56 #define DUK_DEBUG_PORT 9091
57 #endif
58 #if !defined(DUK_DEBUG_ADDRESS)
59 #define DUK_DEBUG_ADDRESS "0.0.0.0"
60 #endif
61 #define DUK__STRINGIFY_HELPER(x) #x
62 #define DUK__STRINGIFY(x) DUK__STRINGIFY_HELPER(x)
63
64 #if 0
65 #define DEBUG_PRINTS
66 #endif
67
68 static SOCKET server_sock = INVALID_SOCKET;
69 static SOCKET client_sock = INVALID_SOCKET;
70 static int wsa_inited = 0;
71
72 /*
73 * Transport init and finish
74 */
75
76 void duk_trans_socket_init(void) {
77 WSADATA wsa_data;
78 struct addrinfo hints;
79 struct addrinfo *result = NULL;
80 int rc;
81
82 memset((void *) &wsa_data, 0, sizeof(wsa_data));
83 memset((void *) &hints, 0, sizeof(hints));
84
85 rc = WSAStartup(MAKEWORD(2, 2), &wsa_data);
86 if (rc != 0) {
87 fprintf(stderr, "%s: WSAStartup() failed: %d\n", __FILE__, rc);
88 fflush(stderr);
89 goto fail;
90 }
91 wsa_inited = 1;
92
93 hints.ai_family = AF_UNSPEC;
94 hints.ai_socktype = SOCK_STREAM;
95 hints.ai_protocol = IPPROTO_TCP;
96 hints.ai_flags = AI_PASSIVE;
97
98 rc = getaddrinfo(DUK_DEBUG_ADDRESS, DUK__STRINGIFY(DUK_DEBUG_PORT), &hints, &result);
99 if (rc != 0) {
100 fprintf(stderr, "%s: getaddrinfo() failed: %d\n", __FILE__, rc);
101 fflush(stderr);
102 goto fail;
103 }
104
105 server_sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
106 if (server_sock == INVALID_SOCKET) {
107 fprintf(stderr, "%s: socket() failed with error: %ld\n",
108 __FILE__, (long) WSAGetLastError());
109 fflush(stderr);
110 goto fail;
111 }
112
113 rc = bind(server_sock, result->ai_addr, (int) result->ai_addrlen);
114 if (rc == SOCKET_ERROR) {
115 fprintf(stderr, "%s: bind() failed with error: %ld\n",
116 __FILE__, (long) WSAGetLastError());
117 fflush(stderr);
118 goto fail;
119 }
120
121 rc = listen(server_sock, SOMAXCONN);
122 if (rc == SOCKET_ERROR) {
123 fprintf(stderr, "%s: listen() failed with error: %ld\n",
124 __FILE__, (long) WSAGetLastError());
125 fflush(stderr);
126 goto fail;
127 }
128
129 if (result != NULL) {
130 freeaddrinfo(result);
131 result = NULL;
132 }
133 return;
134
135 fail:
136 if (result != NULL) {
137 freeaddrinfo(result);
138 result = NULL;
139 }
140 if (server_sock != INVALID_SOCKET) {
141 (void) closesocket(server_sock);
142 server_sock = INVALID_SOCKET;
143 }
144 if (wsa_inited) {
145 WSACleanup();
146 wsa_inited = 0;
147 }
148 }
149
150 void duk_trans_socket_finish(void) {
151 if (client_sock != INVALID_SOCKET) {
152 (void) closesocket(client_sock);
153 client_sock = INVALID_SOCKET;
154 }
155 if (server_sock != INVALID_SOCKET) {
156 (void) closesocket(server_sock);
157 server_sock = INVALID_SOCKET;
158 }
159 if (wsa_inited) {
160 WSACleanup();
161 wsa_inited = 0;
162 }
163 }
164
165 void duk_trans_socket_waitconn(void) {
166 if (server_sock == INVALID_SOCKET) {
167 fprintf(stderr, "%s: no server socket, skip waiting for connection\n",
168 __FILE__);
169 fflush(stderr);
170 return;
171 }
172 if (client_sock != INVALID_SOCKET) {
173 (void) closesocket(client_sock);
174 client_sock = INVALID_SOCKET;
175 }
176
177 fprintf(stderr, "Waiting for debug connection on port %d\n", (int) DUK_DEBUG_PORT);
178 fflush(stderr);
179
180 client_sock = accept(server_sock, NULL, NULL);
181 if (client_sock == INVALID_SOCKET) {
182 fprintf(stderr, "%s: accept() failed with error %ld, skip waiting for connection\n",
183 __FILE__, (long) WSAGetLastError());
184 fflush(stderr);
185 goto fail;
186 }
187
188 fprintf(stderr, "Debug connection established\n");
189 fflush(stderr);
190
191 /* XXX: For now, close the listen socket because we won't accept new
192 * connections anyway. A better implementation would allow multiple
193 * debug attaches.
194 */
195
196 if (server_sock != INVALID_SOCKET) {
197 (void) closesocket(server_sock);
198 server_sock = INVALID_SOCKET;
199 }
200 return;
201
202 fail:
203 if (client_sock != INVALID_SOCKET) {
204 (void) closesocket(client_sock);
205 client_sock = INVALID_SOCKET;
206 }
207 }
208
209 /*
210 * Duktape callbacks
211 */
212
213 /* Duktape debug transport callback: (possibly partial) read. */
214 duk_size_t duk_trans_socket_read_cb(void *udata, char *buffer, duk_size_t length) {
215 int ret;
216
217 (void) udata; /* not needed by the example */
218
219 #if defined(DEBUG_PRINTS)
220 fprintf(stderr, "%s: udata=%p, buffer=%p, length=%ld\n",
221 __FUNCTION__, (void *) udata, (void *) buffer, (long) length);
222 fflush(stderr);
223 #endif
224
225 if (client_sock == INVALID_SOCKET) {
226 return 0;
227 }
228
229 if (length == 0) {
230 /* This shouldn't happen. */
231 fprintf(stderr, "%s: read request length == 0, closing connection\n",
232 __FILE__);
233 fflush(stderr);
234 goto fail;
235 }
236
237 if (buffer == NULL) {
238 /* This shouldn't happen. */
239 fprintf(stderr, "%s: read request buffer == NULL, closing connection\n",
240 __FILE__);
241 fflush(stderr);
242 goto fail;
243 }
244
245 /* In a production quality implementation there would be a sanity
246 * timeout here to recover from "black hole" disconnects.
247 */
248
249 ret = recv(client_sock, (void *) buffer, (int) length, 0);
250 if (ret < 0) {
251 fprintf(stderr, "%s: debug read failed, error %d, closing connection\n",
252 __FILE__, ret);
253 fflush(stderr);
254 goto fail;
255 } else if (ret == 0) {
256 fprintf(stderr, "%s: debug read failed, ret == 0 (EOF), closing connection\n",
257 __FILE__);
258 fflush(stderr);
259 goto fail;
260 } else if (ret > (int) length) {
261 fprintf(stderr, "%s: debug read failed, ret too large (%ld > %ld), closing connection\n",
262 __FILE__, (long) ret, (long) length);
263 fflush(stderr);
264 goto fail;
265 }
266
267 return (duk_size_t) ret;
268
269 fail:
270 if (client_sock != INVALID_SOCKET) {
271 (void) closesocket(client_sock);
272 client_sock = INVALID_SOCKET;
273 }
274 return 0;
275 }
276
277 /* Duktape debug transport callback: (possibly partial) write. */
278 duk_size_t duk_trans_socket_write_cb(void *udata, const char *buffer, duk_size_t length) {
279 int ret;
280
281 (void) udata; /* not needed by the example */
282
283 #if defined(DEBUG_PRINTS)
284 fprintf(stderr, "%s: udata=%p, buffer=%p, length=%ld\n",
285 __FUNCTION__, (void *) udata, (const void *) buffer, (long) length);
286 fflush(stderr);
287 #endif
288
289 if (client_sock == INVALID_SOCKET) {
290 return 0;
291 }
292
293 if (length == 0) {
294 /* This shouldn't happen. */
295 fprintf(stderr, "%s: write request length == 0, closing connection\n",
296 __FILE__);
297 fflush(stderr);
298 goto fail;
299 }
300
301 if (buffer == NULL) {
302 /* This shouldn't happen. */
303 fprintf(stderr, "%s: write request buffer == NULL, closing connection\n",
304 __FILE__);
305 fflush(stderr);
306 goto fail;
307 }
308
309 /* In a production quality implementation there would be a sanity
310 * timeout here to recover from "black hole" disconnects.
311 */
312
313 ret = send(client_sock, (const void *) buffer, (int) length, 0);
314 if (ret <= 0 || ret > (int) length) {
315 fprintf(stderr, "%s: debug write failed, ret %d, closing connection\n",
316 __FILE__, ret);
317 fflush(stderr);
318 goto fail;
319 }
320
321 return (duk_size_t) ret;
322
323 fail:
324 if (client_sock != INVALID_SOCKET) {
325 (void) closesocket(INVALID_SOCKET);
326 client_sock = INVALID_SOCKET;
327 }
328 return 0;
329 }
330
331 duk_size_t duk_trans_socket_peek_cb(void *udata) {
332 u_long avail;
333 int rc;
334
335 (void) udata; /* not needed by the example */
336
337 #if defined(DEBUG_PRINTS)
338 fprintf(stderr, "%s: udata=%p\n", __FUNCTION__, (void *) udata);
339 fflush(stderr);
340 #endif
341
342 if (client_sock == INVALID_SOCKET) {
343 return 0;
344 }
345
346 avail = 0;
347 rc = ioctlsocket(client_sock, FIONREAD, &avail);
348 if (rc != 0) {
349 fprintf(stderr, "%s: ioctlsocket() returned %d, closing connection\n",
350 __FILE__, rc);
351 fflush(stderr);
352 goto fail; /* also returns 0, which is correct */
353 } else {
354 if (avail == 0) {
355 return 0; /* nothing to read */
356 } else {
357 return 1; /* something to read */
358 }
359 }
360 /* never here */
361
362 fail:
363 if (client_sock != INVALID_SOCKET) {
364 (void) closesocket(client_sock);
365 client_sock = INVALID_SOCKET;
366 }
367 return 0;
368 }
369
370 void duk_trans_socket_read_flush_cb(void *udata) {
371 (void) udata; /* not needed by the example */
372
373 #if defined(DEBUG_PRINTS)
374 fprintf(stderr, "%s: udata=%p\n", __FUNCTION__, (void *) udata);
375 fflush(stderr);
376 #endif
377
378 /* Read flush: Duktape may not be making any more read calls at this
379 * time. If the transport maintains a receive window, it can use a
380 * read flush as a signal to update the window status to the remote
381 * peer. A read flush is guaranteed to occur before Duktape stops
382 * reading for a while; it may occur in other situations as well so
383 * it's not a 100% reliable indication.
384 */
385
386 /* This TCP transport requires no read flush handling so ignore.
387 * You can also pass a NULL to duk_debugger_attach() and not
388 * implement this callback at all.
389 */
390 }
391
392 void duk_trans_socket_write_flush_cb(void *udata) {
393 (void) udata; /* not needed by the example */
394
395 #if defined(DEBUG_PRINTS)
396 fprintf(stderr, "%s: udata=%p\n", __FUNCTION__, (void *) udata);
397 fflush(stderr);
398 #endif
399
400 /* Write flush. If the transport combines multiple writes
401 * before actually sending, a write flush is an indication
402 * to write out any pending bytes: Duktape may not be doing
403 * any more writes on this occasion.
404 */
405
406 /* This TCP transport requires no write flush handling so ignore.
407 * You can also pass a NULL to duk_debugger_attach() and not
408 * implement this callback at all.
409 */
410 return;
411 }
412
413 #undef DUK__STRINGIFY_HELPER
414 #undef DUK__STRINGIFY