trace_ip_drv.c
来自「OTP是开放电信平台的简称」· C语言 代码 · 共 911 行 · 第 1/2 页
C
911 行
/* ``The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in * compliance with the License. You should have received a copy of the * Erlang Public License along with this software. If not, it can be * retrieved via the world wide web at http://www.erlang.org/. * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings * AB. All Rights Reserved.'' * * $Id$ *//*** This is a trace port, which means it cannot be busy and takes** no opcodes.** Send trace messages over the net.*/#ifdef HAVE_CONFIG_H# include "config.h"#endif#ifdef __WIN32__# include <winsock2.h># include <windows.h>#endif#include <stdio.h>#include <stdlib.h>#include <string.h>#ifndef __WIN32__# ifdef VXWORKS# include <sockLib.h># include <sys/times.h># include <iosLib.h># include <taskLib.h># include <selectLib.h># include <ioLib.h># include "reclaim.h"# endif # include <unistd.h># include <errno.h># include <sys/types.h># include <sys/socket.h># include <netinet/in.h># include <fcntl.h>#endif#ifdef DEBUG# ifndef __WIN32__ /* erl_exit is not available to dll_drivers on windows. */ void erl_exit(int, char *, ...);# define ASSERT(X) \ do { \ if (!(X)) { \ erl_exit(1,"%s",#X); \ } \ } while(0) # else # include <assert.h># define ASSERT(X) assert(X)# endif#else # define ASSERT(X)#endif #define sock2event(s) ((ErlDrvEvent)(long)(s))#define event2sock(p) ((SOCKET)(long)(p))#include "erl_driver.h"/*** Protocol from driver:** '\0' -> ok** '\1' ++ String -> {error, Atom}** Protocol when opening (arguments to start):** <Portno> <Quesize> <Fl>** Where...** Portno, ascii string representing a number:** The TCP port number to listen to.** Quesize, ascii string representing a number:** The number of messages to que up before dropping.** Fl, ascii string representing a flag byte:** 0x1 -> Drop oldest when que is full (instead of last arrived)** 0x2 -> Fill the que even if noone is listening.**** The package sent over the network looks like this:** +--+--------+-----------------------------------+** |Op|Size NBO|Term in external format or empty |** +--+--------+-----------------------------------+** Op, a char:** 0 = binary, 1 = drop** If Op is 1, then Size reflects the number of dropped messages.** Size, a 32 bit interger in network byte order:** Either the size of the binary term, or the number of packet's dropped. ** Term, an array of bytes:** An erlang term in the external format or simply empty if Op == 1, the** term is Size long.*/ /*** SO, most of the differences between WinDoze and Posixish OS'es ** is handeled here, but the multiplexing (driver_select) is also quite** interesting, see my_driver_select further down in the file...*/#ifndef __WIN32__# define FLAG_READ DO_READ# define FLAG_WRITE DO_WRITE typedef int SOCKET;# define INVALID_SOCKET -1# define IS_INVALID_SOCKET(S) ((S) < 0)# define closesocket(Sock) close(Sock)# define ERRNO errno# ifdef EWOULDBLOCK# define ERRNO_BLOCK EWOULDBLOCK# else# ifdef EAGAIN# define ERRNO_BLOCK EAGAIN# else# error "No EWOULDBLOCK found"# endif# endif#else /* Win32 */# define FLAG_READ (FD_READ | FD_CONNECT | FD_ACCEPT)# define FLAG_WRITE FD_WRITE# define ERRNO WSAGetLastError()# define ERRNO_BLOCK WSAEWOULDBLOCK# define IS_INVALID_SOCKET(S) ((S) == INVALID_SOCKET)#endif/*** Option flags*/#define FLAG_DROP_OLDEST 1#define FLAG_FILL_ALWAYS 2#define FLAG_LISTEN_PORT 4/*** Op's in messages*/#define OP_BINARY 0#define OP_DROP 1 /*** State structure*/typedef struct trace_ip_message { int siz; /* the size of the "binary data" */ int written; /* if only a part was written, when == siz, all is written */ unsigned char bin[1]; /* The opcode, the Size and optionally the binary. */} TraceIpMessage; typedef struct trace_ip_data { unsigned flags; int listen_portno; SOCKET listenfd; SOCKET fd;#ifdef __WIN32__ unsigned listen_event_mask; HANDLE listen_event; unsigned event_mask; HANDLE event;#endif ErlDrvPort port; struct trace_ip_data *next; int quesiz; int questart; int questop; TraceIpMessage *que[1]; /* You guessed it, will be longer... */} TraceIpData;static TraceIpData *first_data; /*** Interface routines*/static ErlDrvData trace_ip_start(ErlDrvPort port, char *buff);static void trace_ip_stop(ErlDrvData handle);static void trace_ip_output(ErlDrvData handle, char *buff, int bufflen);#ifdef __WIN32__static void trace_ip_event(ErlDrvData handle, ErlDrvEvent event);#endifstatic void trace_ip_ready_input(ErlDrvData handle, ErlDrvEvent fd);static void trace_ip_ready_output(ErlDrvData handle, ErlDrvEvent fd);static void trace_ip_finish(void); /* No arguments, despite what might be stated in any documentation */static int trace_ip_control(ErlDrvData handle, unsigned int command, char* buff, int count, char** res, int res_size);/*** Internal routines*/static void *my_alloc(size_t size);static ErlDrvBinary *my_alloc_binary(int size);static int write_until_done(SOCKET s, char *buff, int bufflen);static unsigned get_be(unsigned char *s);static void put_be32(unsigned n, unsigned char *s);static void put_be16(unsigned n, unsigned char *s);static TraceIpData *lookup_data_by_port(int portno);static int set_nonblocking(SOCKET sock);static TraceIpMessage *make_buffer(int datasiz, unsigned char op, unsigned number);static void enque_message(TraceIpData *data, unsigned char *buff, int bufflen, int byteswritten);static void clean_que(TraceIpData *data);static void close_client(TraceIpData *data);static int trywrite(TraceIpData *data, unsigned char *buff, int bufflen);static SOCKET my_accept(SOCKET sock);static void close_unlink_port(TraceIpData *data);static int my_driver_select(TraceIpData *desc, SOCKET fd, int flags, int on);/*** The driver struct*/ErlDrvEntry trace_ip_driver_entry = { NULL, /* F_PTR init, N/A */ trace_ip_start, /* L_PTR start, called when port is opened */ trace_ip_stop, /* F_PTR stop, called when port is closed */ trace_ip_output, /* F_PTR output, called when erlang has sent */#ifdef __WIN32__ trace_ip_event, /* Anything happens on an associated event */ NULL, /* Write selections not supported on win32 */#else trace_ip_ready_input, /* F_PTR ready_input, called when input descriptor ready */ trace_ip_ready_output, /* F_PTR ready_output, called when output descriptor ready */#endif "trace_ip_drv", /* char *driver_name, the argument to open_port */ trace_ip_finish, /* F_PTR finish, called when unloaded */ NULL, /* void * that is not used */ trace_ip_control, /* F_PTR control, port_control callback */ NULL, /* F_PTR timeout, reserved */ NULL /* F_PTR outputv, reserved */};/*** Driver initialization routine**** No matter what's stated otherwise, this function shall return a pointer.*/DRIVER_INIT(trace_ip_drv){ first_data = NULL; /*trace_ip_driver_entry.handle = handle; ??? What is this, and why? It is no more! */ return &trace_ip_driver_entry;}/*** Driver interface routines*//*** Open a port*/static ErlDrvData trace_ip_start(ErlDrvPort port, char *buff){ TraceIpData *ret; int portno; int quesiz; int flags; SOCKET s; struct sockaddr_in sin; int reuse = 1;#ifdef HARDDEBUG fprintf(stderr,"trace_ip_drv/trace_ip_start (%s)\r\n", buff);#endif if (sscanf(buff,"trace_ip_drv %d %d %d",&portno, &quesiz, &flags) != 3) return ERL_DRV_ERROR_GENERAL; if (flags > 3 || flags < 0 || portno < 0 || quesiz < 0) return ERL_DRV_ERROR_GENERAL; if (lookup_data_by_port(portno) != NULL) return ERL_DRV_ERROR_GENERAL; if (IS_INVALID_SOCKET(s = socket(AF_INET, SOCK_STREAM, 0))) return ERL_DRV_ERROR_GENERAL; if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &reuse, sizeof(reuse)) < 0) { closesocket(s); return ERL_DRV_ERROR_GENERAL; } memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = htons((short) portno); if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) != 0) { closesocket(s); return ERL_DRV_ERROR_GENERAL; } if (portno == 0) {#ifdef HAVE_SOCKLEN_T socklen_t sinlen = sizeof(sin);#else int sinlen = (int) sizeof(sin);#endif if (getsockname(s, (struct sockaddr *)&sin, &sinlen) != 0) { closesocket(s); return ERL_DRV_ERROR_GENERAL; } else { portno = ntohs(sin.sin_port); } } if (listen(s, 1)) { /* No significant backlog needed */ closesocket(s); return ERL_DRV_ERROR_GENERAL; } if (set_nonblocking(s) != 0){ closesocket(s); return ERL_DRV_ERROR_GENERAL; } /* OK, the socket is created, lets build the structure. */ /* Deliberately one more pointer than the quesize specified... */ ret = my_alloc(sizeof(TraceIpData) + quesiz * sizeof(TraceIpMessage *)); ret->flags = flags | FLAG_LISTEN_PORT; ret->listen_portno = portno; ret->listenfd = s; ret->fd = INVALID_SOCKET; ret->port = port; ret->next = first_data; ret->quesiz = quesiz+1; ret->questart = ret->questop = 0; memset(ret->que, 0, ret->quesiz); first_data = ret;#ifdef __WIN32__ ret->listen_event_mask = 0; ret->listen_event = 0; ret->event_mask = 0; ret->event = 0;#endif my_driver_select(ret, s, FLAG_READ, 1); set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY); return (ErlDrvData) ret;}/*** Close a port*/static void trace_ip_stop(ErlDrvData handle){ close_unlink_port((TraceIpData *) handle);}/*** Data sent from erlang to port.*/static void trace_ip_output(ErlDrvData handle, char *buff, int bufflen){ TraceIpData *data = (TraceIpData *) handle; if (data->flags & FLAG_LISTEN_PORT) { if (data->flags & FLAG_FILL_ALWAYS) { enque_message(data, buff, bufflen, 0); } return; } if (data->que[data->questart] != NULL) { trace_ip_ready_output(handle, sock2event(data->fd)); } if (data->que[data->questart] == NULL) { int written = trywrite(data, buff, bufflen); if (written >= 0 && written < bufflen + 5) { enque_message(data, buff, bufflen, written); my_driver_select(data, data->fd, FLAG_WRITE, 1); } return; } enque_message(data, buff, bufflen, 0); return;}/*** We have something to read from a file descriptor*/static void trace_ip_ready_input(ErlDrvData handle, ErlDrvEvent fd){ TraceIpData *data = (TraceIpData *) handle; int client; if (!(data->flags & FLAG_LISTEN_PORT) && event2sock(fd) == data->listenfd) { /* ** Someone tries to connect to already connected port, ** just accept and close. */ if (!IS_INVALID_SOCKET((client = my_accept(data->listenfd)))) { closesocket(client); } return; } if (event2sock(fd) == data->listenfd) { /* ** Maybe accept, we are a listen port... */ if (!IS_INVALID_SOCKET((client = my_accept(data->listenfd)))) { data->fd = client; set_nonblocking(client); if (data->que[data->questart] != NULL) { my_driver_select(data, data->fd, FLAG_WRITE | FLAG_READ, 1); } else { my_driver_select(data, data->fd, FLAG_READ, 1); } data->flags &= ~(FLAG_LISTEN_PORT); } return; } /* * It is a probably EOF because the other end closed the socket, * but better make sure. */ if ((SOCKET)fd == data->fd) {#ifdef __WIN32__ close_client(data);#else int res; char sbuf[128]; if ((res = read(data->fd, sbuf, sizeof sbuf)) == 0) { close_client(data); } /* * Something else. Just ignore it. * * When /dev/poll is used on Solaris, this callback can * be called even if there is nothing to read. An attempt * to read will result in an EAGAIN error. */#ifdef DEBUG if (res < 0) { fprintf(stderr, "Read on fd %d failed with errno=%d\r\n", data->fd, errno);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?