1 /*
2 * %CopyrightBegin%
3 *
4 * Copyright Ericsson AB 1999-2016. All Rights Reserved.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * %CopyrightEnd%
19 */
20 /*
21 ** This is a trace port, which means it cannot be busy and takes
22 ** no opcodes.
23 ** Send trace messages over the net.
24 */
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #ifdef __WIN32__
30 # include <winsock2.h>
31 # include <windows.h>
32 #endif
33
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #ifndef __WIN32__
38 # include <unistd.h>
39 # include <errno.h>
40 # include <sys/types.h>
41 # include <sys/socket.h>
42 # include <netinet/in.h>
43 # include <fcntl.h>
44 #endif
45
46 #ifdef DEBUG
47 # include <assert.h>
48 # define ASSERT(X) assert(X)
49 #else
50 # define ASSERT(X)
51 #endif
52
53 #define sock2event(s) ((ErlDrvEvent)(long)(s))
54 #define event2sock(p) ((SOCKET)(long)(p))
55
56 #include "erl_driver.h"
57
58 /*
59 ** Protocol from driver:
60 ** '\0' -> ok
61 ** '\1' ++ String -> {error, Atom}
62 ** Protocol when opening (arguments to start):
63 ** <Portno> <Quesize> <Fl>
64 ** Where...
65 ** Portno, ascii string representing a number:
66 ** The TCP port number to listen to.
67 ** Quesize, ascii string representing a number:
68 ** The number of messages to que up before dropping.
69 ** Fl, ascii string representing a flag byte:
70 ** 0x1 -> Drop oldest when que is full (instead of last arrived)
71 ** 0x2 -> Fill the que even if noone is listening.
72 **
73 ** The package sent over the network looks like this:
74 ** +--+--------+-----------------------------------+
75 ** |Op|Size NBO|Term in external format or empty |
76 ** +--+--------+-----------------------------------+
77 ** Op, a char:
78 ** 0 = binary, 1 = drop
79 ** If Op is 1, then Size reflects the number of dropped messages.
80 ** Size, a 32 bit interger in network byte order:
81 ** Either the size of the binary term, or the number of packet's dropped.
82 ** Term, an array of bytes:
83 ** An erlang term in the external format or simply empty if Op == 1, the
84 ** term is Size long.
85 */
86
87 /*
88 ** SO, most of the differences between WinDoze and Posixish OS'es
89 ** is handeled here, but the multiplexing (driver_select) is also quite
90 ** interesting, see my_driver_select further down in the file...
91 */
92
93 #ifndef __WIN32__
94 # define FLAG_READ ERL_DRV_READ
95 # define FLAG_WRITE ERL_DRV_WRITE
96 typedef int SOCKET;
97 # define INVALID_SOCKET -1
98 # define IS_INVALID_SOCKET(S) ((S) < 0)
99 # define closesocket(Sock) close(Sock)
100 # define ERRNO errno
101 # ifdef EWOULDBLOCK
102 # define ERRNO_BLOCK EWOULDBLOCK
103 # else
104 # ifdef EAGAIN
105 # define ERRNO_BLOCK EAGAIN
106 # else
107 # error "No EWOULDBLOCK found"
108 # endif
109 # endif
110 #else /* Win32 */
111 # define FLAG_READ (FD_READ | FD_CONNECT | FD_ACCEPT)
112 # define FLAG_WRITE FD_WRITE
113 # define ERRNO WSAGetLastError()
114 # define ERRNO_BLOCK WSAEWOULDBLOCK
115 # define IS_INVALID_SOCKET(S) ((S) == INVALID_SOCKET)
116 #endif
117
118
119 /*
120 ** Option flags
121 */
122 #define FLAG_DROP_OLDEST 1
123 #define FLAG_FILL_ALWAYS 2
124 #define FLAG_LISTEN_PORT 4
125
126 /*
127 ** Op's in messages
128 */
129 #define OP_BINARY 0
130 #define OP_DROP 1
131
132 /*
133 ** State structure
134 */
135
136 typedef struct trace_ip_message {
137 int siz; /* the size of the "binary data" */
138 int written; /* if only a part was written, when == siz, all is written */
139 unsigned char bin[1]; /* The opcode, the Size and optionally the binary. */
140 } TraceIpMessage;
141
142
143 typedef struct trace_ip_data {
144 unsigned flags;
145 int listen_portno;
146 SOCKET listenfd;
147 SOCKET fd;
148 #ifdef __WIN32__
149 unsigned listen_event_mask;
150 HANDLE listen_event;
151 unsigned event_mask;
152 HANDLE event;
153 #endif
154 ErlDrvPort port;
155 struct trace_ip_data *next;
156 int quesiz;
157 int questart;
158 int questop;
159 TraceIpMessage *que[1]; /* You guessed it, will be longer... */
160 } TraceIpData;
161
162 static TraceIpData *first_data;
163
164 /*
165 ** Interface routines
166 */
167 static ErlDrvData trace_ip_start(ErlDrvPort port, char *buff);
168 static void trace_ip_stop(ErlDrvData handle);
169 static void trace_ip_output(ErlDrvData handle, char *buff,
170 ErlDrvSizeT bufflen);
171 #ifdef __WIN32__
172 static void trace_ip_event(ErlDrvData handle, ErlDrvEvent event);
173 #endif
174 static void trace_ip_ready_input(ErlDrvData handle, ErlDrvEvent fd);
175 static void trace_ip_ready_output(ErlDrvData handle, ErlDrvEvent fd);
176 static void trace_ip_finish(void); /* No arguments, despite what might be stated
177 in any documentation */
178 static ErlDrvSSizeT trace_ip_control(ErlDrvData handle,
179 unsigned int command,
180 char* buff, ErlDrvSizeT count,
181 char** res, ErlDrvSizeT res_size);
182
183 /*
184 ** Internal routines
185 */
186 static void *my_alloc(size_t size);
187 static ErlDrvBinary *my_alloc_binary(int size);
188 static int write_until_done(SOCKET s, char *buff, int bufflen);
189 static unsigned get_be(unsigned char *s);
190 static void put_be32(unsigned n, unsigned char *s);
191 static void put_be16(unsigned n, unsigned char *s);
192 static TraceIpData *lookup_data_by_port(int portno);
193 static int set_nonblocking(SOCKET sock);
194 static TraceIpMessage *make_buffer(int datasiz, unsigned char op,
195 unsigned number);
196 static void enque_message(TraceIpData *data, char *buff, int bufflen,
197 int byteswritten);
198 static void clean_que(TraceIpData *data);
199 static void close_client(TraceIpData *data);
200 static int trywrite(TraceIpData *data, char *buff, int bufflen);
201 static SOCKET my_accept(SOCKET sock);
202 static void close_unlink_port(TraceIpData *data);
203 enum MySelectOp { SELECT_ON, SELECT_OFF, SELECT_CLOSE };
204 static int my_driver_select(TraceIpData *desc, SOCKET fd, int flags, enum MySelectOp);
205 static void stop_select(ErlDrvEvent event, void*);
206
207 /*
208 ** The driver struct
209 */
210 ErlDrvEntry trace_ip_driver_entry = {
211 NULL, /* F_PTR init, N/A */
212 trace_ip_start, /* L_PTR start, called when port is opened */
213 trace_ip_stop, /* F_PTR stop, called when port is closed */
214 trace_ip_output, /* F_PTR output, called when erlang has sent */
215 #ifdef __WIN32__
216 trace_ip_event, /* Anything happens on an associated event */
217 NULL, /* Write selections not supported on win32 */
218 #else
219 trace_ip_ready_input, /* F_PTR ready_input, called when input descriptor
220 ready */
221 trace_ip_ready_output, /* F_PTR ready_output, called when output
222 descriptor ready */
223 #endif
224 "trace_ip_drv", /* char *driver_name, the argument to open_port */
225 trace_ip_finish, /* F_PTR finish, called when unloaded */
226 NULL, /* void * that is not used */
227 trace_ip_control, /* F_PTR control, port_control callback */
228 NULL, /* F_PTR timeout, reserved */
229 NULL, /* F_PTR outputv, reserved */
230 NULL, /* ready_async */
231 NULL, /* flush */
232 NULL, /* call */
233 NULL, /* event */
234 ERL_DRV_EXTENDED_MARKER,
235 ERL_DRV_EXTENDED_MAJOR_VERSION,
236 ERL_DRV_EXTENDED_MINOR_VERSION,
237 0, /* ERL_DRV_FLAGs */
238 NULL,
239 NULL, /* process_exit */
240 stop_select
241 };
242
243 /*
244 ** Driver initialization routine
245 **
246 ** No matter what's stated otherwise, this function shall return a pointer.
247 */
248
DRIVER_INIT(trace_ip_drv)249 DRIVER_INIT(trace_ip_drv)
250 {
251 first_data = NULL;
252 /*trace_ip_driver_entry.handle = handle; ??? What is this, and why? It is no more! */
253 return &trace_ip_driver_entry;
254 }
255
256 /*
257 ** Driver interface routines
258 */
259
260 /*
261 ** Open a port
262 */
trace_ip_start(ErlDrvPort port,char * buff)263 static ErlDrvData trace_ip_start(ErlDrvPort port, char *buff)
264 {
265 TraceIpData *ret;
266 int portno;
267 int quesiz;
268 int flags;
269 SOCKET s;
270 struct sockaddr_in sin;
271 int reuse = 1;
272
273 #ifdef HARDDEBUG
274 fprintf(stderr,"trace_ip_drv/trace_ip_start (%s)\r\n", buff);
275 #endif
276 if (sscanf(buff,"trace_ip_drv %d %d %d",&portno, &quesiz, &flags) != 3)
277 return ERL_DRV_ERROR_GENERAL;
278
279 if (flags > 3 || flags < 0 || portno < 0 || quesiz < 0)
280 return ERL_DRV_ERROR_GENERAL;
281
282 if (lookup_data_by_port(portno) != NULL)
283 return ERL_DRV_ERROR_GENERAL;
284
285 if (IS_INVALID_SOCKET(s = socket(AF_INET, SOCK_STREAM, 0)))
286 return ERL_DRV_ERROR_GENERAL;
287
288 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
289 (char *) &reuse, sizeof(reuse)) < 0) {
290 closesocket(s);
291 return ERL_DRV_ERROR_GENERAL;
292 }
293
294
295 memset(&sin, 0, sizeof(sin));
296 sin.sin_family = AF_INET;
297 sin.sin_addr.s_addr = INADDR_ANY;
298 sin.sin_port = htons((short) portno);
299
300 if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) != 0) {
301 closesocket(s);
302 return ERL_DRV_ERROR_GENERAL;
303 }
304
305 if (portno == 0) {
306 #ifdef HAVE_SOCKLEN_T
307 socklen_t sinlen = sizeof(sin);
308 #else
309 int sinlen = (int) sizeof(sin);
310 #endif
311 if (getsockname(s, (struct sockaddr *)&sin, &sinlen) != 0) {
312 closesocket(s);
313 return ERL_DRV_ERROR_GENERAL;
314 } else {
315 portno = ntohs(sin.sin_port);
316 }
317 }
318
319 if (listen(s, 1)) { /* No significant backlog needed */
320 closesocket(s);
321 return ERL_DRV_ERROR_GENERAL;
322 }
323
324 if (set_nonblocking(s) != 0){
325 closesocket(s);
326 return ERL_DRV_ERROR_GENERAL;
327 }
328
329 /* OK, the socket is created, lets build the structure. */
330 /* Deliberately one more pointer than the quesize specified... */
331 ret = my_alloc(sizeof(TraceIpData) +
332 quesiz * sizeof(TraceIpMessage *));
333
334 ret->flags = flags | FLAG_LISTEN_PORT;
335 ret->listen_portno = portno;
336 ret->listenfd = s;
337 ret->fd = INVALID_SOCKET;
338 ret->port = port;
339 ret->next = first_data;
340 ret->quesiz = quesiz+1;
341 ret->questart = ret->questop = 0;
342 memset(ret->que, 0, ret->quesiz);
343
344 first_data = ret;
345 #ifdef __WIN32__
346 ret->listen_event_mask = 0;
347 ret->listen_event = 0;
348 ret->event_mask = 0;
349 ret->event = 0;
350 #endif
351 my_driver_select(ret, s, FLAG_READ, SELECT_ON);
352 set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);
353
354 return (ErlDrvData) ret;
355 }
356
357 /*
358 ** Close a port
359 */
trace_ip_stop(ErlDrvData handle)360 static void trace_ip_stop(ErlDrvData handle)
361 {
362 close_unlink_port((TraceIpData *) handle);
363 }
364
365 /*
366 ** Data sent from erlang to port.
367 */
trace_ip_output(ErlDrvData handle,char * buff,ErlDrvSizeT bufflen)368 static void trace_ip_output(ErlDrvData handle, char *buff, ErlDrvSizeT bufflen)
369 {
370 TraceIpData *data = (TraceIpData *) handle;
371 if (data->flags & FLAG_LISTEN_PORT) {
372 if (data->flags & FLAG_FILL_ALWAYS) {
373 enque_message(data, buff, bufflen, 0);
374 }
375 return;
376 }
377 ASSERT(!IS_INVALID_SOCKET(data->fd));
378 if (data->que[data->questart] != NULL) {
379 trace_ip_ready_output(handle, sock2event(data->fd));
380 }
381 if (data->que[data->questart] == NULL) {
382 int written = trywrite(data, buff, bufflen);
383 if (written >= 0 && written < bufflen + 5) {
384 enque_message(data, buff, bufflen, written);
385 my_driver_select(data, data->fd, FLAG_WRITE, SELECT_ON);
386 }
387 return;
388 }
389 enque_message(data, buff, bufflen, 0);
390 return;
391 }
392
393 /*
394 ** We have something to read from a file descriptor
395 */
trace_ip_ready_input(ErlDrvData handle,ErlDrvEvent fd)396 static void trace_ip_ready_input(ErlDrvData handle, ErlDrvEvent fd)
397 {
398 TraceIpData *data = (TraceIpData *) handle;
399 int client;
400
401 if (!(data->flags & FLAG_LISTEN_PORT) && event2sock(fd) == data->listenfd) {
402 /*
403 ** Someone tries to connect to already connected port,
404 ** just accept and close.
405 */
406 if (!IS_INVALID_SOCKET((client = my_accept(data->listenfd)))) {
407 closesocket(client);
408 }
409 return;
410 }
411
412 if (event2sock(fd) == data->listenfd) {
413 /*
414 ** Maybe accept, we are a listen port...
415 */
416 ASSERT(IS_INVALID_SOCKET(data->fd));
417 if (!IS_INVALID_SOCKET((client = my_accept(data->listenfd)))) {
418 data->fd = client;
419 set_nonblocking(client);
420 if (data->que[data->questart] != NULL) {
421 my_driver_select(data, data->fd, FLAG_WRITE | FLAG_READ, SELECT_ON);
422 } else {
423 my_driver_select(data, data->fd, FLAG_READ, SELECT_ON);
424 }
425 data->flags &= ~(FLAG_LISTEN_PORT);
426 }
427 return;
428 }
429
430 /*
431 * It is a probably EOF because the other end closed the socket,
432 * but better make sure.
433 */
434
435 if ((SOCKET)(long)fd == data->fd) {
436 #ifdef __WIN32__
437 close_client(data);
438 #else
439 int res;
440 char sbuf[128];
441
442 if ((res = read(data->fd, sbuf, sizeof sbuf)) == 0) {
443 close_client(data);
444 }
445
446 /*
447 * Something else. Just ignore it.
448 *
449 * When /dev/poll is used on Solaris, this callback can
450 * be called even if there is nothing to read. An attempt
451 * to read will result in an EAGAIN error.
452 */
453 #ifdef DEBUG
454 if (res < 0) {
455 fprintf(stderr, "Read on fd %d failed with errno=%d\r\n",
456 data->fd, errno);
457 }
458 #endif
459 #endif
460 return;
461 }
462
463 ASSERT(0);
464 }
465
466 #ifdef __WIN32__
trace_ip_event(ErlDrvData handle,ErlDrvEvent event)467 static void trace_ip_event(ErlDrvData handle, ErlDrvEvent event)
468 {
469 TraceIpData *data = (TraceIpData *) handle;
470 if ((HANDLE)event == data->event) {
471 WSANETWORKEVENTS netEv;
472 if (WSAEnumNetworkEvents(data->fd, data->event, &netEv) != 0) {
473 return;
474 }
475 if (netEv.lNetworkEvents & FLAG_WRITE) {
476 trace_ip_ready_output(handle, (ErlDrvEvent)data->fd);
477 }
478 if (netEv.lNetworkEvents & FLAG_READ) {
479 trace_ip_ready_input(handle, (ErlDrvEvent)data->fd);
480 }
481 } else if ((HANDLE)event == data->listen_event) {
482 trace_ip_ready_input(handle, (ErlDrvEvent)data->listenfd);
483 }
484 }
485 #endif /* ifdef __WIN32__ */
486
487 /*
488 ** We can write to a file descriptor
489 */
trace_ip_ready_output(ErlDrvData handle,ErlDrvEvent fd)490 static void trace_ip_ready_output(ErlDrvData handle, ErlDrvEvent fd)
491 {
492 TraceIpData *data = (TraceIpData *) handle;
493 int res;
494 int towrite;
495 TraceIpMessage *tim;
496
497 ASSERT(!(data->flags & FLAG_LISTEN_PORT) &&
498 data->que[data->questart] != NULL &&
499 (SOCKET)(long)fd == data->fd);
500
501 tim = data->que[data->questart];
502 towrite = tim->siz - tim->written;
503 while((res = write_until_done(data->fd,
504 (char *)tim->bin + tim->written, towrite))
505 == towrite) {
506 driver_free(tim);
507 data->que[data->questart] = NULL;
508 if (data->questart == data->questop) {
509 /*
510 ** We wrote the last message in the que, dont
511 ** step forward, just 'deselect'
512 */
513 my_driver_select(data, data->fd, FLAG_WRITE, SELECT_OFF);
514 /*
515 ** We are really done...
516 */
517 return;
518 }
519 if (++(data->questart) == data->quesiz)
520 data->questart = 0;
521 tim = data->que[data->questart];
522 ASSERT(tim != NULL);
523 towrite = tim->siz - tim->written;
524 }
525 if (res < 0) {
526 close_client(data);
527 return;
528 }
529
530 tim->written += res;
531 }
532
533 /*
534 ** Control message from erlang, we handle $p, which is get_listen_port.
535 */
trace_ip_control(ErlDrvData handle,unsigned int command,char * buff,ErlDrvSizeT count,char ** res,ErlDrvSizeT res_size)536 static ErlDrvSSizeT trace_ip_control(ErlDrvData handle,
537 unsigned int command,
538 char* buff, ErlDrvSizeT count,
539 char** res, ErlDrvSizeT res_size)
540 {
541 register void *void_ptr; /* Soft type cast */
542
543 if (command == 'p') {
544 TraceIpData *data = (TraceIpData *) handle;
545 ErlDrvBinary *b = my_alloc_binary(3);
546 b->orig_bytes[0] = '\0'; /* OK */
547 put_be16(data->listen_portno, (unsigned char *)&(b->orig_bytes[1]));
548 *res = void_ptr = b;
549 return 0;
550 }
551 return -1;
552 }
553
554 /*
555 ** Driver unloaded
556 */
trace_ip_finish(void)557 static void trace_ip_finish(void)
558 {
559 while (first_data != NULL) {
560 close_unlink_port(first_data);
561 }
562 }
563
564 /*
565 ** Internal helpers
566 */
567
568 /*
569 ** Yet another malloc wrapper
570 */
my_alloc(size_t size)571 static void *my_alloc(size_t size)
572 {
573 void *ret;
574 if ((ret = driver_alloc(size)) == NULL) {
575 /* May or may not work... */
576 fprintf(stderr, "Could not allocate %lu bytes of memory in %s.",
577 (unsigned long) size, __FILE__);
578 exit(1);
579 }
580 return ret;
581 }
582
583 /*
584 ** Yet another malloc wrapper
585 */
my_alloc_binary(int size)586 static ErlDrvBinary *my_alloc_binary(int size)
587 {
588 ErlDrvBinary *ret;
589 if ((ret = driver_alloc_binary(size)) == NULL) {
590 /* May or may not work... */
591 fprintf(stderr, "Could not allocate a binary of %lu bytes in %s.",
592 (unsigned long) size, __FILE__);
593 exit(1);
594 }
595 return ret;
596 }
597
598 /*
599 ** Write to a nonblocking descriptor until it states EWOULDBLOCK
600 */
write_until_done(SOCKET s,char * buff,int bufflen)601 static int write_until_done(SOCKET s, char *buff, int bufflen)
602 {
603 int ret = 0;
604 int res = 0;
605
606 #ifdef HARDDEBUG
607 fprintf(stderr, "Writing %d characters.\r\n", bufflen);
608 #endif
609
610 while(ret < bufflen && (res = send(s, buff + ret, bufflen - ret, 0)) > 0) {
611 ret += res;
612 }
613 if (ret < bufflen) {
614 if (res == 0) {
615 fprintf(stderr, "internal error in trace_ip_drv, "
616 "write to nonblocking "
617 "returned 0!");
618 exit(1);
619 } else if (ERRNO != ERRNO_BLOCK) {
620 return -1;
621 }
622 }
623 return ret;
624 }
625
626 /*
627 ** Translate big endian integer in buffer to unsigned
628 */
get_be(unsigned char * s)629 static unsigned get_be(unsigned char *s)
630 {
631 return s[3] | (s[2] << 8) | (s[1] << 16) | s[0] << 24;
632 }
633
634 /*
635 ** Write unsigned to buffer in big endian
636 */
637
put_be32(unsigned n,unsigned char * s)638 static void put_be32(unsigned n, unsigned char *s)
639 {
640 s[0] = n >> 24U;
641 s[1] = n >> 16U;
642 s[2] = n >> 8U;
643 s[3] = n;
644 }
645
put_be16(unsigned n,unsigned char * s)646 static void put_be16(unsigned n, unsigned char *s)
647 {
648 s[0] = n >> 8U;
649 s[1] = n;
650 }
651
652 /*
653 ** Lookup a port's data structure by the *TCP/IP port number*
654 */
lookup_data_by_port(int portno)655 static TraceIpData *lookup_data_by_port(int portno)
656 {
657 TraceIpData *tmp = first_data;
658 while (tmp != NULL && tmp->listen_portno != portno)
659 tmp = tmp->next;
660 return tmp;
661 }
662
663 /*
664 ** Create a TraceIpMessage buffer (the binary data NOT filled in)
665 */
make_buffer(int datasiz,unsigned char op,unsigned number)666 static TraceIpMessage *make_buffer(int datasiz, unsigned char op,
667 unsigned number)
668 {
669 TraceIpMessage *ret = my_alloc(sizeof(TraceIpMessage) +
670 (datasiz + 4));
671 ret->siz = datasiz + 5;
672 ret->written = 0;
673 ret->bin[0] = op;
674 put_be32(number, ret->bin + 1);
675 return ret;
676 }
677
678 /*
679 ** Add message to que, discarding in a politically correct way...
680 ** The FLAG_DROP_OLDEST is currently ingored...
681 */
enque_message(TraceIpData * data,char * buff,int bufflen,int byteswritten)682 static void enque_message(TraceIpData *data, char *buff, int bufflen,
683 int byteswritten)
684 {
685 int diff = data->questop - data->questart;
686 TraceIpMessage *tim;
687
688 if (diff == -1 || diff == data->quesiz - 1) {
689 put_be32(get_be((data->que[data->questop])->bin + 1) + 1,
690 (data->que[data->questop])->bin + 1);
691 } else if (diff == -2 || diff == data->quesiz - 2) {
692 ASSERT(byteswritten == 0);
693 if (++(data->questop) == data->quesiz) {
694 data->questop = 0;
695 }
696 data->que[data->questop] = make_buffer(0, OP_DROP, 1);
697 } else {
698 if (data->que[data->questop] != NULL &&
699 ++(data->questop) == data->quesiz) {
700 data->questop = 0;
701 }
702 tim = make_buffer(bufflen, OP_BINARY, bufflen);
703 tim->written = byteswritten;
704 memcpy(tim->bin + 5, buff, bufflen);
705 data->que[data->questop] = tim;
706 }
707 }
708
709 /*
710 ** Clean a que
711 */
clean_que(TraceIpData * data)712 static void clean_que(TraceIpData *data)
713 {
714 int b = data->questart;
715 int e = data->questop;
716
717 while (b != e) {
718 if (data->que[b] != NULL) {
719 driver_free(data->que[b]);
720 data->que[b] = NULL;
721 }
722 if (++b >= data->quesiz) {
723 b = 0;
724 }
725 }
726 if (data->que[b] != NULL) {
727 driver_free(data->que[b]);
728 data->que[b] = NULL;
729 }
730 data->questart = data->questop = 0;
731 }
732
733 /*
734 ** Cleanup closed client (or close the client and cleanup)
735 */
close_client(TraceIpData * data)736 static void close_client(TraceIpData *data)
737 {
738 my_driver_select(data, data->fd, FLAG_WRITE | FLAG_READ, SELECT_CLOSE);
739 data->flags |= FLAG_LISTEN_PORT;
740 data->fd = INVALID_SOCKET;
741 if (!(data->flags & FLAG_FILL_ALWAYS)) {
742 clean_que(data);
743 }
744 }
745
746 /*
747 ** Try to write a message from erlang directly (only called when que is empty
748 ** and client is connected)
749 */
trywrite(TraceIpData * data,char * buff,int bufflen)750 static int trywrite(TraceIpData *data, char *buff, int bufflen)
751 {
752 char op[5];
753 int res;
754
755 op[0] = OP_BINARY;
756 put_be32(bufflen, (unsigned char *)op + 1);
757
758 if ((res = write_until_done(data->fd, op, 5)) < 0) {
759 close_client(data);
760 return -1;
761 }
762 if (res < 5) {
763 return res;
764 }
765
766 if ((res = write_until_done(data->fd, buff, bufflen)) < 0) {
767 close_client(data);
768 return -1;
769 }
770
771 return res + 5;
772 }
773
774 /*
775 ** accept wrapper
776 */
my_accept(SOCKET sock)777 static SOCKET my_accept(SOCKET sock)
778 {
779 struct sockaddr_in sin;
780 #ifdef HAVE_SOCKLEN_T
781 socklen_t sin_size = sizeof(sin);
782 #else
783 int sin_size = (int) sizeof(sin);
784 #endif
785
786 return accept(sock, (struct sockaddr *) &sin, &sin_size);
787 }
788
789 /*
790 ** Close the whole port and clean up
791 */
close_unlink_port(TraceIpData * data)792 static void close_unlink_port(TraceIpData *data)
793 {
794 TraceIpData **tmp;
795
796 data->flags = 0;
797 if (!IS_INVALID_SOCKET(data->fd)) {
798 close_client(data);
799 }
800 my_driver_select(data, data->listenfd, FLAG_READ, SELECT_CLOSE);
801
802 for(tmp = &first_data; *tmp != NULL && *tmp != data;
803 tmp = &((*tmp)->next))
804 ;
805 if (*tmp != NULL) {
806 *tmp = (*tmp)->next;
807 }
808 driver_free(data);
809 }
810
811
812
813
814 #ifdef __WIN32__
815 /*
816 ** Mostly stolen from inet_drv in the emulator.
817 */
my_driver_select(TraceIpData * desc,SOCKET fd,int flags,enum MySelectOp op)818 static int my_driver_select(TraceIpData *desc, SOCKET fd,
819 int flags, enum MySelectOp op)
820 {
821 HANDLE *event;
822 unsigned *event_mask;
823 int ret = -1;
824 unsigned save_event_mask;
825
826 if(fd == desc->listenfd) {
827 event = &(desc->listen_event);
828 event_mask = &(desc->listen_event_mask);
829 } else if(fd == desc->fd) {
830 event = &(desc->event);
831 event_mask = &(desc->event_mask);
832 } else {
833 return -1;
834 }
835
836 save_event_mask = *event_mask;
837
838 if (op==SELECT_ON) {
839 *event_mask |= flags;
840 } else {
841 *event_mask &= (~flags);
842 }
843 if (*event_mask != 0 && *event == 0) {
844 *event = WSACreateEvent();
845 driver_select(desc->port, *event, ERL_DRV_READ|ERL_DRV_USE, 1);
846 }
847
848 /* The RIGHT WAY (TM) to do this is to make sure:
849 A) The cancelling of all network events is done with
850 NULL as the event parameter (bug in NT's winsock),
851 B) The actual event handle is reset so that it is only
852 raised if one of the requested network events is active,
853 C) Avoid race conditions by making sure that the event cannot be set
854 while we are preparing to set the correct network event mask.
855 The simplest way to do it is to turn off all events, reset the
856 event handle and then, if event_mask != 0, turn on the appropriate
857 events again. */
858 if (WSAEventSelect(fd, NULL, 0) != 0) {
859 *event_mask = save_event_mask;
860 goto error;
861 }
862 if (!ResetEvent(*event)) {
863 *event_mask = 0;
864 goto error;
865 }
866 if (*event_mask != 0) {
867 if (WSAEventSelect(fd,
868 *event,
869 *event_mask) != 0) {
870 *event_mask = 0;
871 goto error;
872 }
873 }
874 ret = 0;
875 error:
876 if (*event_mask == 0 && *event != 0 && (op==SELECT_CLOSE || ret!=0)) {
877 WSAEventSelect(fd, NULL, 0); /* Not necessary?
878 Well, actually I dont know, and
879 MS documentation states nothing
880 about what happens if WSAEventSelect
881 is called with empty event mask and
882 then the event is deleted. */
883 driver_select(desc->port, *event, ERL_DRV_READ|ERL_DRV_USE, 0);
884 *event = 0;
885 if (op == SELECT_CLOSE) {
886 closesocket(fd);
887 }
888 }
889 return ret;
890 }
891
stop_select(ErlDrvEvent event,void * _)892 static void stop_select(ErlDrvEvent event, void* _)
893 {
894 WSACloseEvent((HANDLE)event);
895 }
896
897 #else /* UNIX */
898
my_driver_select(TraceIpData * desc,SOCKET fd,int flags,enum MySelectOp op)899 static int my_driver_select(TraceIpData *desc, SOCKET fd, int flags, enum MySelectOp op)
900 {
901 if (op != SELECT_OFF) {
902 flags |= ERL_DRV_USE;
903 }
904 return driver_select(desc->port, sock2event(fd), flags, (op==SELECT_ON));
905 }
906
stop_select(ErlDrvEvent event,void * _)907 static void stop_select(ErlDrvEvent event, void* _)
908 {
909 closesocket((SOCKET)(long)event);
910 }
911
912 #endif /* !__WIN32__ */
913
914 /*
915 ** Set socket nonblocking, keep this at end of file.
916 */
917 #undef ERRNO_BLOCK
918 #undef ASSERT
919 #ifndef WANT_NONBLOCKING
920 #define WANT_NONBLOCKING
921 #endif
922 #include "sys.h"
set_nonblocking(SOCKET sock)923 static int set_nonblocking(SOCKET sock)
924 {
925 SET_NONBLOCKING(sock);
926 return 0;
927 }
928
929
930