]> git.proxmox.com Git - ceph.git/blob - ceph/src/civetweb/src/third_party/duktape-1.3.0/examples/debug-trans-socket/duk_trans_socket.c
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / civetweb / src / third_party / duktape-1.3.0 / examples / debug-trans-socket / duk_trans_socket.c
1 /*
2 * Example debug transport using a TCP socket
3 *
4 * The application has a server socket which can be connected to.
5 * After that data is just passed through.
6 *
7 * NOTE: This is Linux specific on purpose, as it's just an example how
8 * a debug transport can be concretely implemented.
9 */
10
11 #include <stdio.h>
12 #include <string.h>
13 #include <sys/socket.h>
14 #include <arpa/inet.h>
15 #include <unistd.h>
16 #include <poll.h>
17 #include <errno.h>
18 #include "duktape.h"
19
20 #ifndef DUK_DEBUG_PORT
21 #define DUK_DEBUG_PORT 9091
22 #endif
23
24 #if 0
25 #define DEBUG_PRINTS
26 #endif
27
28 static int server_sock = -1;
29 static int client_sock = -1;
30
31 /*
32 * Transport init
33 */
34
35 void duk_trans_socket_init(void) {
36 struct sockaddr_in addr;
37 int on;
38
39 server_sock = socket(AF_INET, SOCK_STREAM, 0);
40 if (server_sock < 0) {
41 fprintf(stderr, "%s: failed to create server socket: %s\n", __FILE__, strerror(errno));
42 fflush(stderr);
43 goto fail;
44 }
45
46 on = 1;
47 if (setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, (const char *) &on, sizeof(on)) < 0) {
48 fprintf(stderr, "%s: failed to set SO_REUSEADDR for server socket: %s\n", __FILE__, strerror(errno));
49 fflush(stderr);
50 goto fail;
51 }
52
53 memset((void *) &addr, 0, sizeof(addr));
54 addr.sin_family = AF_INET;
55 addr.sin_addr.s_addr = INADDR_ANY;
56 addr.sin_port = htons(DUK_DEBUG_PORT);
57
58 if (bind(server_sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
59 fprintf(stderr, "%s: failed to bind server socket: %s\n", __FILE__, strerror(errno));
60 fflush(stderr);
61 goto fail;
62 }
63
64 listen(server_sock, 1 /*backlog*/);
65 return;
66
67 fail:
68 if (server_sock >= 0) {
69 (void) close(server_sock);
70 server_sock = -1;
71 }
72 }
73
74 void duk_trans_socket_waitconn(void) {
75 struct sockaddr_in addr;
76 socklen_t sz;
77
78 if (server_sock < 0) {
79 fprintf(stderr, "%s: no server socket, skip waiting for connection\n", __FILE__);
80 fflush(stderr);
81 return;
82 }
83 if (client_sock >= 0) {
84 (void) close(client_sock);
85 client_sock = -1;
86 }
87
88 fprintf(stderr, "Waiting for debug connection on port %d\n", (int) DUK_DEBUG_PORT);
89 fflush(stderr);
90
91 sz = (socklen_t) sizeof(addr);
92 client_sock = accept(server_sock, (struct sockaddr *) &addr, &sz);
93 if (client_sock < 0) {
94 fprintf(stderr, "%s: accept() failed, skip waiting for connection: %s\n", __FILE__, strerror(errno));
95 fflush(stderr);
96 goto fail;
97 }
98
99 fprintf(stderr, "Debug connection established\n");
100 fflush(stderr);
101
102 /* XXX: For now, close the listen socket because we won't accept new
103 * connections anyway. A better implementation would allow multiple
104 * debug attaches.
105 */
106
107 if (server_sock >= 0) {
108 (void) close(server_sock);
109 server_sock = -1;
110 }
111 return;
112
113 fail:
114 if (client_sock >= 0) {
115 (void) close(client_sock);
116 client_sock = -1;
117 }
118 }
119
120 /*
121 * Duktape callbacks
122 */
123
124 /* Duktape debug transport callback: partial read */
125 duk_size_t duk_trans_socket_read_cb(void *udata, char *buffer, duk_size_t length) {
126 ssize_t ret;
127
128 (void) udata; /* not needed by the example */
129
130 #if defined(DEBUG_PRINTS)
131 fprintf(stderr, "%s: udata=%p, buffer=%p, length=%ld\n",
132 __func__, (void *) udata, (void *) buffer, (long) length);
133 fflush(stderr);
134 #endif
135
136 if (client_sock < 0) {
137 return 0;
138 }
139
140 if (length == 0) {
141 /* This shouldn't happen. */
142 fprintf(stderr, "%s: read request length == 0, closing connection\n", __FILE__);
143 fflush(stderr);
144 goto fail;
145 }
146
147 if (buffer == NULL) {
148 /* This shouldn't happen. */
149 fprintf(stderr, "%s: read request buffer == NULL, closing connection\n", __FILE__);
150 fflush(stderr);
151 goto fail;
152 }
153
154 /* In a production quality implementation there would be a sanity
155 * timeout here to recover from "black hole" disconnects.
156 */
157
158 ret = read(client_sock, (void *) buffer, (size_t) length);
159 if (ret < 0) {
160 fprintf(stderr, "%s: debug read failed, errno %d, closing connection: %s\n", __FILE__, errno, strerror(errno));
161 fflush(stderr);
162 goto fail;
163 } else if (ret == 0) {
164 fprintf(stderr, "%s: debug read failed, ret == 0 (EOF), closing connection\n", __FILE__);
165 fflush(stderr);
166 goto fail;
167 } else if (ret > (ssize_t) length) {
168 fprintf(stderr, "%s: debug read failed, ret too large (%ld > %ld), closing connection\n", __FILE__, (long) ret, (long) length);
169 fflush(stderr);
170 goto fail;
171 }
172
173 return (duk_size_t) ret;
174
175 fail:
176 if (client_sock >= 0) {
177 (void) close(client_sock);
178 client_sock = -1;
179 }
180 return 0;
181 }
182
183 /* Duktape debug transport callback: partial write */
184 duk_size_t duk_trans_socket_write_cb(void *udata, const char *buffer, duk_size_t length) {
185 ssize_t ret;
186
187 (void) udata; /* not needed by the example */
188
189 #if defined(DEBUG_PRINTS)
190 fprintf(stderr, "%s: udata=%p, buffer=%p, length=%ld\n",
191 __func__, (void *) udata, (void *) buffer, (long) length);
192 fflush(stderr);
193 #endif
194
195 if (client_sock < 0) {
196 return 0;
197 }
198
199 if (length == 0) {
200 /* This shouldn't happen. */
201 fprintf(stderr, "%s: write request length == 0, closing connection\n", __FILE__);
202 fflush(stderr);
203 goto fail;
204 }
205
206 if (buffer == NULL) {
207 /* This shouldn't happen. */
208 fprintf(stderr, "%s: write request buffer == NULL, closing connection\n", __FILE__);
209 fflush(stderr);
210 goto fail;
211 }
212
213 /* In a production quality implementation there would be a sanity
214 * timeout here to recover from "black hole" disconnects.
215 */
216
217 ret = write(client_sock, (const void *) buffer, (size_t) length);
218 if (ret <= 0 || ret > (ssize_t) length) {
219 fprintf(stderr, "%s: debug write failed, closing connection: %s\n", __FILE__, strerror(errno));
220 fflush(stderr);
221 goto fail;
222 }
223
224 return (duk_size_t) ret;
225
226 fail:
227 if (client_sock >= 0) {
228 (void) close(client_sock);
229 client_sock = -1;
230 }
231 return 0;
232 }
233
234 duk_size_t duk_trans_socket_peek_cb(void *udata) {
235 struct pollfd fds[1];
236 int poll_rc;
237
238 (void) udata; /* not needed by the example */
239
240 #if defined(DEBUG_PRINTS)
241 fprintf(stderr, "%s: udata=%p\n", __func__, (void *) udata);
242 fflush(stderr);
243 #endif
244
245 fds[0].fd = client_sock;
246 fds[0].events = POLLIN;
247 fds[0].revents = 0;
248
249 poll_rc = poll(fds, 1, 0);
250 if (poll_rc < 0) {
251 fprintf(stderr, "%s: poll returned < 0, closing connection: %s\n", __FILE__, strerror(errno));
252 fflush(stderr);
253 goto fail; /* also returns 0, which is correct */
254 } else if (poll_rc > 1) {
255 fprintf(stderr, "%s: poll returned > 1, treating like 1\n", __FILE__);
256 fflush(stderr);
257 return 1; /* should never happen */
258 } else if (poll_rc == 0) {
259 return 0; /* nothing to read */
260 } else {
261 return 1; /* something to read */
262 }
263
264 fail:
265 if (client_sock >= 0) {
266 (void) close(client_sock);
267 client_sock = -1;
268 }
269 return 0;
270 }
271
272 void duk_trans_socket_read_flush_cb(void *udata) {
273 #if defined(DEBUG_PRINTS)
274 fprintf(stderr, "%s: udata=%p\n", __func__, (void *) udata);
275 fflush(stderr);
276 #endif
277
278 (void) udata; /* not needed by the example */
279
280 /* Read flush: Duktape may not be making any more read calls at this
281 * time. If the transport maintains a receive window, it can use a
282 * read flush as a signal to update the window status to the remote
283 * peer. A read flush is guaranteed to occur before Duktape stops
284 * reading for a while; it may occur in other situations as well so
285 * it's not a 100% reliable indication.
286 */
287
288 /* This TCP transport requires no read flush handling so ignore.
289 * You can also pass a NULL to duk_debugger_attach() and not
290 * implement this callback at all.
291 */
292 }
293
294 void duk_trans_socket_write_flush_cb(void *udata) {
295 #if defined(DEBUG_PRINTS)
296 fprintf(stderr, "%s: udata=%p\n", __func__, (void *) udata);
297 fflush(stderr);
298 #endif
299
300 (void) udata; /* not needed by the example */
301
302 /* Write flush. If the transport combines multiple writes
303 * before actually sending, a write flush is an indication
304 * to write out any pending bytes: Duktape may not be doing
305 * any more writes on this occasion.
306 */
307
308 /* This TCP transport requires no write flush handling so ignore.
309 * You can also pass a NULL to duk_debugger_attach() and not
310 * implement this callback at all.
311 */
312 return;
313 }