diff --git a/stio/lib/Makefile b/stio/lib/Makefile new file mode 100644 index 0000000..c0c02b7 --- /dev/null +++ b/stio/lib/Makefile @@ -0,0 +1,2 @@ +all: + cc -o stio main.c stio.c stio-tcp.c stio-udp.c stio-sck.c diff --git a/stio/lib/main.c b/stio/lib/main.c new file mode 100644 index 0000000..6df6328 --- /dev/null +++ b/stio/lib/main.c @@ -0,0 +1,175 @@ +#include "stio.h" +#include +#include +#include +#include +#include +#include + +static void* mmgr_alloc (stio_mmgr_t* mmgr, stio_size_t size) +{ + return malloc (size); +} + +static void mmgr_free (stio_mmgr_t* mmgr, void* ptr) +{ + return free (ptr); +} + +static stio_mmgr_t mmgr = +{ + mmgr_alloc, + mmgr_free, + STIO_NULL +}; + + + +static void tcp_on_connected (stio_dev_tcp_t* tcp) +{ + printf ("REALLY CONNECTED.......\n"); +} + +static void tcp_on_disconnected (stio_dev_tcp_t* tcp) +{ + if (tcp->state & STIO_DEV_TCP_LISTENING) + { + printf ("SHUTTING DOWN THE SERVER SOCKET(%d)...\n", tcp->sck); + } + else if (tcp->state & STIO_DEV_TCP_CONNECTED) + { + printf ("CLIENT ORIGINATING FROM HERE GOT DISCONNECTED(%d).......\n", tcp->sck); + } + else if (tcp->state & STIO_DEV_TCP_ACCEPTED) + { + printf ("CLIENT BEING SERVED GOT DISCONNECTED(%d).......\n", tcp->sck); + } + else + { + printf ("TCP DISCONNECTED - THIS MUST NOT HAPPEN (%d)\n", tcp->sck); + } +} +static void tcp_on_accepted (stio_dev_tcp_t* tcp, stio_dev_tcp_t* clitcp) +{ + printf ("device accepted client device... ....\n"); +} + + +static stio_t* g_stio; + +static void handle_signal (int sig) +{ + if (g_stio) stio_stop (g_stio); +} + +int main () +{ + + stio_t* stio; + stio_dev_udp_t* udp; + stio_dev_tcp_t* tcp; + struct sockaddr_in sin; + struct sigaction sigact; + stio_dev_tcp_connect_t tcp_conn; + stio_dev_tcp_listen_t tcp_lstn; + + + stio = stio_open (&mmgr, 0, STIO_NULL); + if (!stio) + { + printf ("Cannot open stio\n"); + return -1; + } + + g_stio = stio; + + STIO_MEMSET (&sigact, 0, STIO_SIZEOF(sigact)); + sigact.sa_flags = SA_RESTART; + sigact.sa_handler = handle_signal; + sigaction (SIGINT, &sigact, STIO_NULL); + +/* + pkt = stio_pkt_open (packet type, protocol type); // packet socket + arp = stio_arp_open (binding_addr); // raw socket - arp filter + tcpd = stio_tcp_open (binding_addr); // crude tcp + udpd= stio_udp_open (binding_addr); // crude udp + httpd = stio_httpd_open (binding_addr); + httpsd = stio_httpsd_open (binding_addr); + radiusd = stio_radiusd_open (binding_addr); // udp - radius + + stio_register (stio, httpd); + stio_register (stio, httpsd); + stio_register (stio, radiusd); +*/ + + STIO_MEMSET (&sin, 0, STIO_SIZEOF(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(1234); +/* + udp = (stio_dev_udp_t*)stio_makedev (stio, STIO_SIZEOF(*udp), &udp_mth, &udp_evcb, &sin); + if (!udp) + { + printf ("Cannot make udp\n"); + goto oops; + } +*/ + + STIO_MEMSET (&sin, 0, STIO_SIZEOF(sin)); + sin.sin_family = AF_INET; +#if 0 + + tcp = (stio_dev_tcp_t*)stio_makedev (stio, STIO_SIZEOF(*tcp), &tcp_mth, &tcp_evcb, &sin); + if (!tcp) + { + printf ("Cannot make tcp\n"); + goto oops; + } + + { + struct sockaddr_in* p; + p = (struct sockaddr_in*)&tcp_conn.addr; + p->sin_family = AF_INET; + p->sin_port = htons(9999); + inet_pton (p->sin_family, "127.0.0.1", &p->sin_addr); + tcp_conn.on_connected = tcp_on_connected; + tcp_conn.on_disconnected = tcp_on_disconnected; + //tcp_conn.on_failure = .... (error code? etc???) or on_connect to access success or failure??? what is better?? + } + if (stio_dev_tcp_connect (tcp, &tcp_conn) <= -1) + { + printf ("stio_dev_tcp_connect() failed....\n"); + goto oops; + } +#else + sin.sin_port = htons(1234); + tcp = stio_dev_tcp_make (stio, (stio_sckadr_t*)&sin); + if (!tcp) + { + printf ("Cannot make tcp\n"); + goto oops; + } + + tcp_lstn.backlogs = 100; + tcp_lstn.on_accepted = tcp_on_accepted; + tcp_lstn.on_disconnected = tcp_on_disconnected; + if (stio_dev_tcp_listen (tcp, &tcp_lstn) <= -1) + { + printf ("stio_dev_tcp_listen() failed....\n"); + goto oops; + } +#endif + + //////////////////////////// + + stio_loop (stio); + //////////////////////////// + + g_stio = STIO_NULL; + stio_close (stio); + return 0; + +oops: + g_stio = STIO_NULL; + stio_close (stio); + return -1; +} diff --git a/stio/lib/stio-prv.h b/stio/lib/stio-prv.h new file mode 100644 index 0000000..a0a0265 --- /dev/null +++ b/stio/lib/stio-prv.h @@ -0,0 +1,44 @@ +#ifndef _STIO_PRV_H_ +#define _STIO_PRV_H_ + +#include "stio.h" +#include + +struct stio_t +{ + stio_mmgr_t* mmgr; + stio_errnum_t errnum; + int stopreq; /* stop request to abort stio_loop() */ + + struct + { + stio_dev_t* head; + stio_dev_t* tail; + } dev; + + stio_uint8_t bigbuf[65535]; /* make this dynamic depending on devices added. device may indicate a buffer size required??? */ + + +#if defined(_WIN32) + HANDLE iocp; +#else + int mux; + struct epoll_event revs[100]; +#endif +}; + + +#ifdef __cplusplus +extern "C" { +#endif + +stio_sckhnd_t stio_openasyncsck (int domain, int type); +void stio_closeasyncsck (stio_sckhnd_t sck); +int stio_makesckasync (stio_sckhnd_t sck); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/stio/lib/stio-sck.c b/stio/lib/stio-sck.c new file mode 100644 index 0000000..e1acaac --- /dev/null +++ b/stio/lib/stio-sck.c @@ -0,0 +1,58 @@ +#include "stio-prv.h" +#include +#include +#include + +/* ------------------------------------------------------------------------ */ +void stio_closeasyncsck (stio_sckhnd_t sck) +{ +#if defined(_WIN32) + closesocket (sck); +#else + close (sck); +#endif +} + +int stio_makesckasync (stio_sckhnd_t sck) +{ + int flags; + + if ((flags = fcntl (sck, F_GETFL)) <= -1 || + (flags = fcntl (sck, F_SETFL, flags | O_NONBLOCK)) <= -1) + { + /* stio_seterrnum (dev->stio, STIO_ESYSERR); or translate errno to stio errnum */ + return -1; + } + + return 0; +} + +stio_sckhnd_t stio_openasyncsck (int domain, int type) +{ + stio_sckhnd_t sck; + +#if defined(_WIN32) + sck = WSASocket (domain, type, 0, NULL, 0, WSA_FLAG_OVERLAPPED /*| WSA_FLAG_NO_HANDLE_INHERIT*/); + if (sck == STIO_SCKHND_INVALID) + { + /* stio_seterrnum (dev->stio, STIO_ESYSERR); or translate errno to stio errnum */ + return STIO_SCKHND_INVALID; + } +#else + sck = socket (domain, type, 0); /* NO CLOEXEC or somethign */ + if (sck == STIO_SCKHND_INVALID) + { + /* stio_seterrnum (dev->stio, STIO_ESYSERR); or translate errno to stio errnum */ + return STIO_SCKHND_INVALID; + } + + if (stio_makesckasync (sck) <= -1) + { + close (sck); + return STIO_SCKHND_INVALID; + } +#endif + + return sck; +} + diff --git a/stio/lib/stio-tcp.c b/stio/lib/stio-tcp.c new file mode 100644 index 0000000..57033a6 --- /dev/null +++ b/stio/lib/stio-tcp.c @@ -0,0 +1,450 @@ +#include "stio-prv.h" + +#include +#include +#include +#include + + +static int tcp_make (stio_dev_t* dev, void* ctx) +{ +/* NOTE: this can be extended to use ctx to tell between INET and INET6 or other types of sockets without creating a new dev method set. */ + + stio_dev_tcp_t* tcp = (stio_dev_tcp_t*)dev; + struct sockaddr* saddr = (struct sockaddr*)ctx; + + tcp->sck = stio_openasyncsck (AF_INET, SOCK_STREAM); + if (tcp->sck == STIO_SCKHND_INVALID) goto oops; + + if (saddr) + { + stio_scklen_t len; + int iv; + + if (saddr->sa_family == AF_INET) + len = STIO_SIZEOF(struct sockaddr_in); + else if (saddr->sa_family == AF_INET6) + len = STIO_SIZEOF(struct sockaddr_in6); + else + { + dev->stio->errnum = STIO_EINVAL; + goto oops; + } + + //setsockopt (udp->sck, SOL_SOCKET, SO_REUSEADDR, ...); + // TRANSPARENT, ETC. + + iv = 1; + if (setsockopt (tcp->sck, SOL_SOCKET, SO_REUSEADDR, &iv, STIO_SIZEOF(iv)) == -1 || + bind (tcp->sck, saddr, len) == -1) + { + //dev->stio->errnum = STIO_EINVAL; TODO: + goto oops; + } + } + + return 0; + +oops: + if (tcp->sck != STIO_SCKHND_INVALID) + { + stio_closeasyncsck (tcp->sck); + tcp->sck = STIO_SCKHND_INVALID; + } + return -1; +} + +static int tcp_make_accepted (stio_dev_t* dev, void* ctx) +{ + stio_dev_tcp_t* tcp = (stio_dev_tcp_t*)dev; + stio_syshnd_t* sck = (stio_syshnd_t*)ctx; + + tcp->sck = *sck; + if (stio_makesckasync (tcp->sck) <= -1) return -1; + + return 0; +} + + +static void tcp_kill (stio_dev_t* dev) +{ + stio_dev_tcp_t* tcp = (stio_dev_tcp_t*)dev; + + if (tcp->state | (STIO_DEV_TCP_ACCEPTED | STIO_DEV_TCP_CONNECTED)) + { + if (tcp->on_disconnected) tcp->on_disconnected (tcp); + } + + if (tcp->sck != STIO_SCKHND_INVALID) + { + stio_closeasyncsck (tcp->sck); + tcp->sck = STIO_SCKHND_INVALID; + } +} + +static stio_syshnd_t tcp_getsyshnd (stio_dev_t* dev) +{ + stio_dev_tcp_t* tcp = (stio_dev_tcp_t*)dev; + return (stio_syshnd_t)tcp->sck; +} + + +static int tcp_recv (stio_dev_t* dev, void* buf, stio_len_t* len) +{ + stio_dev_tcp_t* tcp = (stio_dev_tcp_t*)dev; + int x; + +printf ("TCP RECV...\n"); + x = recv (tcp->sck, buf, *len, 0); + if (x <= -1) + { + if (errno == EINPROGRESS || errno == EWOULDBLOCK) return 0; /* no data available */ + return -1; + } + + *len = x; + return 1; +} + +static int tcp_send (stio_dev_t* dev, const void* data, stio_len_t* len) +{ + stio_dev_tcp_t* tcp = (stio_dev_tcp_t*)tcp; + ssize_t x; + +#if 0 + x = sendto (tcp->sck, data, *len, skad, stio_getskadlen(skad)); + if (x <= -1) + { + if (errno == EINPROGRESS || errno == EWOULDBLOCK) return 0; /* no data can be written */ + return -1; + } + +/* for UDP, if the data chunk can't be written at one go, it's actually a failure */ + if (x != *len) return -1; /* TODO: can i hava an indicator for this in stio? */ + + *len = x; +#endif + return 1; +} + + +static int tcp_ioctl (stio_dev_t* dev, int cmd, void* arg) +{ + stio_dev_tcp_t* tcp = (stio_dev_tcp_t*)dev; + + switch (cmd) + { + case STIO_DEV_TCP_BIND: + { + stio_dev_tcp_bind_t* bnd = (stio_dev_tcp_bind_t*)arg; + struct sockaddr* sa = (struct sockaddr*)&bnd->addr; + stio_scklen_t sl; + int x; + + if (sa->sa_family == AF_INET) sl = STIO_SIZEOF(struct sockaddr_in); + else if (sa->sa_family == AF_INET6) sl = STIO_SIZEOF(struct sockaddr_in6); + else + { + dev->stio->errnum = STIO_EINVAL; + return -1; + } + + #if defined(_WIN32) + /* TODO */ + #else + /* the socket is already non-blocking */ + x = bind (tcp->sck, sa, sl); + if (x == -1) + { + /* TODO: set dev->errnum from errno */ + return -1; + } + + return 0; + #endif + + } + + case STIO_DEV_TCP_CONNECT: + { + stio_dev_tcp_connect_t* conn = (stio_dev_tcp_connect_t*)arg; + struct sockaddr* sa = (struct sockaddr*)&conn->addr; + stio_scklen_t sl; + int x; + + if (sa->sa_family == AF_INET) sl = STIO_SIZEOF(struct sockaddr_in); + else if (sa->sa_family == AF_INET6) sl = STIO_SIZEOF(struct sockaddr_in6); + else + { + dev->stio->errnum = STIO_EINVAL; + return -1; + } + + #if defined(_WIN32) + /* TODO */ + #else + /* the socket is already non-blocking */ + + + x = connect (tcp->sck, sa, sl); + if (x == -1) + { + if (errno == EINPROGRESS || errno == EWOULDBLOCK) + { + if (stio_dev_event ((stio_dev_t*)tcp, STIO_DEV_EVENT_MOD, STIO_DEV_EVENT_IN | STIO_DEV_EVENT_OUT) >= 0) + { + tcp->state |= STIO_DEV_TCP_CONNECTING; + tcp->peer = conn->addr; + tcp->on_connected = conn->on_connected; + tcp->on_disconnected = conn->on_disconnected; + return 0; + } + } + + /* TODO: set dev->errnum from errno */ + return -1; + } + + /* connected immediately */ + tcp->state |= STIO_DEV_TCP_CONNECTED; + tcp->peer = conn->addr; + tcp->on_connected = conn->on_connected; + tcp->on_disconnected = conn->on_disconnected; + return 0; + #endif + } + + case STIO_DEV_TCP_LISTEN: + { + stio_dev_tcp_listen_t* lstn = (stio_dev_tcp_listen_t*)arg; + int x; + + #if defined(_WIN32) + /* TODO */ + #else + x = listen (tcp->sck, lstn->backlogs); + if (x == -1) + { + /* TODO: set tcp->stio->errnum */ + return -1; + } + + tcp->state |= STIO_DEV_TCP_LISTENING; + tcp->on_accepted = lstn->on_accepted; + tcp->on_disconnected = lstn->on_disconnected; + return 0; + #endif + } + } + + return 0; +} + +int stio_dev_tcp_bind (stio_dev_tcp_t* tcp, stio_dev_tcp_bind_t* bind) +{ + return stio_dev_ioctl ((stio_dev_t*)tcp, STIO_DEV_TCP_BIND, bind); +} + +int stio_dev_tcp_connect (stio_dev_tcp_t* tcp, stio_dev_tcp_connect_t* conn) +{ + return stio_dev_ioctl ((stio_dev_t*)tcp, STIO_DEV_TCP_CONNECT, conn); +} + +int stio_dev_tcp_listen (stio_dev_tcp_t* tcp, stio_dev_tcp_listen_t* lstn) +{ + return stio_dev_ioctl ((stio_dev_t*)tcp, STIO_DEV_TCP_LISTEN, lstn); +} + + +static stio_dev_mth_t tcp_mth = +{ + tcp_make, + tcp_kill, + tcp_getsyshnd, + tcp_ioctl, + tcp_recv, + tcp_send +}; + +/* accepted tcp socket */ +static stio_dev_mth_t tcp_acc_mth = +{ + tcp_make_accepted, + tcp_kill, + tcp_getsyshnd, + tcp_ioctl, + tcp_recv, + tcp_send +}; + + +/* ------------------------------------------------------------------------ */ + +static int tcp_ready (stio_dev_t* dev, int events) +{ + stio_dev_tcp_t* tcp = (stio_dev_tcp_t*)dev; +printf ("TCP READY...%p\n", dev); + + if (tcp->state & STIO_DEV_TCP_CONNECTING) + { + if (events & (STIO_DEV_EVENT_ERR | STIO_DEV_EVENT_HUP | STIO_DEV_EVENT_PRI | STIO_DEV_EVENT_IN)) + { + int errcode; + stio_scklen_t len; + + len = STIO_SIZEOF(errcode); + if (getsockopt (tcp->sck, SOL_SOCKET, SO_ERROR, (char*)&errcode, &len) == 0) + { + printf ("CANNOT CONNECT ERRORCODE - %s\n", strerror(errcode)); + } + +printf ("Cannot connect....\n"); + return -1; + } + else if (events & STIO_DEV_EVENT_OUT) + { + int errcode; + stio_scklen_t len; + + STIO_ASSERT (!(tcp->state & STIO_DEV_TCP_CONNECTED)); + + printf ("XXXXXXXXXXXXXXX CONNECTED...\n"); + len = STIO_SIZEOF(errcode); + if (getsockopt (tcp->sck, SOL_SOCKET, SO_ERROR, (char*)&errcode, &len) == -1) + { + } + else if (errcode == 0) + { + tcp->state &= ~STIO_DEV_TCP_CONNECTING; + tcp->state |= STIO_DEV_TCP_CONNECTED; + + if (stio_dev_event ((stio_dev_t*)tcp, STIO_DEV_EVENT_MOD, STIO_DEV_EVENT_IN) <= -1) + { + printf ("CAANOT MANIPULTE EVENT ... KILL DEVICE...\n"); + return -1; + } + + if (tcp->on_connected) tcp->on_connected (tcp); + } + else if (errcode == EINPROGRESS || errcode == EWOULDBLOCK) + { + /* still in progress */ + } + else + { + printf ("failed to get SOCKET PROGRESS CODE...\n"); + return -1; + } + } + + return 0; /* success but don't invoke on_recv() */ + } + else if (tcp->state & STIO_DEV_TCP_LISTENING) + { + stio_sckhnd_t clisck; + stio_sckadr_t peer; + stio_scklen_t addrlen; + + /* this is a server(lisening) socket */ + + addrlen = STIO_SIZEOF(peer); + clisck = accept (tcp->sck, (struct sockaddr*)&peer, &addrlen); + if (clisck == -1) + { + if (errno == EINPROGRESS || errno == EWOULDBLOCK) return 0; + + /* TODO: set tcp->stio->errnum from errno */ + return -1; + } + + if (tcp->on_accepted) + { + stio_dev_tcp_t* clitcp; + + /* addr is the address of the peer */ + /* local addresss is inherited from the server */ + + clitcp = (stio_dev_tcp_t*)stio_makedev (tcp->stio, STIO_SIZEOF(*tcp), &tcp_acc_mth, tcp->evcb, &clisck); + if (!clitcp) + { + close (clisck); + return -1; + } + + clitcp->state |= STIO_DEV_TCP_ACCEPTED; + clitcp->peer = peer; + clitcp->parent = tcp; + + /* inherit the parent's on_disconnected() handler. + * you can still change it inside the on_accepted handler */ + clitcp->on_disconnected = tcp->on_disconnected; + + tcp->on_accepted (tcp, clitcp); + } + else + { + /* no on_accepted callback is set. close the client socket + * without doing anything meaningful */ + close (clisck); + } + + return 0; /* success but don't invoke on_recv() */ + } + +printf ("READY WITH %d\n", events); + + + if (events & (STIO_DEV_EVENT_ERR | STIO_DEV_EVENT_HUP)) + { +printf ("DISCONNECTED or ERROR \n"); + stio_killdev (dev->stio, dev); + return 0; + } + + return 1; /* the device is ok. carry on reading or writing */ +} + +static int tcp_on_recv (stio_dev_t* dev, const void* data, stio_len_t len) +{ + stio_dev_tcp_t* tcp = (stio_dev_tcp_t*)dev; + +printf ("TCP dATA received %d bytes\n", (int)len); + + return 0; + +} + +static int tcp_on_sent (stio_dev_t* dev, const void* data, stio_len_t len) +{ + stio_dev_tcp_t* tcp = (stio_dev_tcp_t*)dev; + + /* TODO: do something */ +printf ("TCP dATA sent %d bytes\n", (int)len); + + return 0; +} + +static stio_dev_evcb_t tcp_evcb = +{ + tcp_ready, + tcp_on_recv, + tcp_on_sent +}; + + + + +stio_dev_tcp_t* stio_dev_tcp_make (stio_t* stio, stio_sckadr_t* addr) +{ + stio_dev_tcp_t* tcp; + + tcp = (stio_dev_tcp_t*)stio_makedev (stio, STIO_SIZEOF(*tcp), &tcp_mth, &tcp_evcb, addr); + + return tcp; +} + + +void stio_dev_tcp_kill (stio_dev_tcp_t* tcp) +{ + stio_killdev (tcp->stio, (stio_dev_t*)tcp); +} diff --git a/stio/lib/stio-udp.c b/stio/lib/stio-udp.c new file mode 100644 index 0000000..ee7b529 --- /dev/null +++ b/stio/lib/stio-udp.c @@ -0,0 +1,176 @@ +#include "stio-prv.h" + +#include +#include +#include +#include + + +static int udp_make (stio_dev_t* dev, void* ctx) +{ +/* NOTE: this can be extended to use ctx to tell between INET and INET6 or other types of sockets without creating a new dev method set. */ + + stio_dev_udp_t* udp = (stio_dev_udp_t*)dev; + struct sockaddr* saddr = (struct sockaddr*)ctx; + + udp->sck = stio_openasyncsck (AF_INET, SOCK_DGRAM); + if (udp->sck == STIO_SCKHND_INVALID) goto oops; + + if (saddr) + { + stio_scklen_t len; + if (saddr->sa_family == AF_INET) + len = STIO_SIZEOF(struct sockaddr_in); + else if (saddr->sa_family == AF_INET6) + len = STIO_SIZEOF(struct sockaddr_in6); + else + { + dev->stio->errnum = STIO_EINVAL; + goto oops; + } + + + //setsockopt (udp->sck, SOL_SOCKET, SO_REUSEADDR, ...); + if (bind (udp->sck, saddr, len) == -1) + { + //dev->stio->errnum = STIO_EINVAL; TODO: + goto oops; + } + } + + return 0; + +oops: + if (udp->sck != STIO_SCKHND_INVALID) + { + stio_closeasyncsck (udp->sck); + udp->sck = STIO_SCKHND_INVALID; + } + return -1; +} + +static void udp_kill (stio_dev_t* dev) +{ + stio_dev_udp_t* udp = (stio_dev_udp_t*)dev; + if (udp->sck != STIO_SCKHND_INVALID) + { + stio_closeasyncsck (udp->sck); + udp->sck = STIO_SCKHND_INVALID; + } +} + +static stio_syshnd_t udp_getsyshnd (stio_dev_t* dev) +{ + stio_dev_udp_t* udp = (stio_dev_udp_t*)dev; + return (stio_syshnd_t)udp->sck; +} + +static int udp_recv (stio_dev_t* dev, void* buf, stio_len_t* len) +{ + stio_dev_udp_t* udp = (stio_dev_udp_t*)dev; + stio_scklen_t addrlen; + int x; + +printf ("UDP RECVFROM...\n"); + addrlen = STIO_SIZEOF(udp->peer); + x = recvfrom (udp->sck, buf, *len, 0, (struct sockaddr*)&udp->peer, &addrlen); + if (x <= -1) + { + if (errno == EINPROGRESS || errno == EWOULDBLOCK) return 0; /* no data available */ + return -1; + } + + *len = x; + return 1; +} + +static int udp_send (stio_dev_t* dev, const void* data, stio_len_t* len) +{ + stio_dev_udp_t* udp = (stio_dev_udp_t*)udp; + ssize_t x; + +#if 0 + x = sendto (udp->sck, data, *len, skad, stio_getskadlen(skad)); + if (x <= -1) + { + if (errno == EINPROGRESS || errno == EWOULDBLOCK) return 0; /* no data can be written */ + return -1; + } + +/* for UDP, if the data chunk can't be written at one go, it's actually a failure */ + if (x != *len) return -1; /* TODO: can i hava an indicator for this in stio? */ + + *len = x; +#endif + return 1; +} + + +static int udp_ioctl (stio_dev_t* dev, int cmd, void* arg) +{ + return 0; +} + + +/* ------------------------------------------------------------------------ */ + +// ----------------------------------------------------------------- + +static stio_dev_mth_t udp_mth = +{ + udp_make, + udp_kill, + udp_getsyshnd, + + udp_ioctl, /* ioctl */ + udp_recv, + udp_send +}; + +static int udp_ready (stio_dev_t* dev, int events) +{ + if (events & STIO_DEV_EVENT_ERR) printf ("UDP READY ERROR.....\n"); + if (events & STIO_DEV_EVENT_HUP) printf ("UDP READY HANGUP.....\n"); + if (events & STIO_DEV_EVENT_PRI) printf ("UDP READY PRI.....\n"); + if (events & STIO_DEV_EVENT_IN) printf ("UDP READY IN.....\n"); + if (events & STIO_DEV_EVENT_OUT) printf ("UDP READY OUT.....\n"); + + return 0; +} + +static int udp_on_recv (stio_dev_t* dev, const void* data, stio_len_t len) +{ +printf ("dATA received %d bytes\n", (int)len); + return 0; + +} + +static int udp_on_sent (stio_dev_t* dev, const void* data, stio_len_t len) +{ + return 0; + +} + +static stio_dev_evcb_t udp_evcb = +{ + udp_ready, + udp_on_recv, + udp_on_sent +}; + + + +stio_dev_udp_t* stio_dev_udp_make (stio_t* stio, stio_sckadr_t* addr) +{ + stio_dev_udp_t* udp; + + udp = (stio_dev_udp_t*)stio_makedev (stio, STIO_SIZEOF(*udp), &udp_mth, &udp_evcb, addr); + + return udp; +} + + +void stio_dev_udp_kill (stio_dev_udp_t* udp) +{ + stio_killdev (udp->stio, (stio_dev_t*)udp); +} diff --git a/stio/lib/stio.c b/stio/lib/stio.c new file mode 100644 index 0000000..785d02a --- /dev/null +++ b/stio/lib/stio.c @@ -0,0 +1,347 @@ +#include "stio-prv.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +stio_t* stio_open (stio_mmgr_t* mmgr, stio_size_t xtnsize, stio_errnum_t* errnum) +{ + stio_t* stio; + + stio = STIO_MMGR_ALLOC (mmgr, STIO_SIZEOF(*stio)); + if (!stio) + { + if (errnum) *errnum = STIO_ENOMEM; + return STIO_NULL; + } + + STIO_MEMSET (stio, 0, STIO_SIZEOF(*stio)); + stio->mmgr = mmgr; + + stio->mux = epoll_create (1000); + if (stio->mux == -1) + { + //if (errnum) *errnum = XXXXX + STIO_MMGR_FREE (stio->mmgr, stio); + return STIO_NULL; + } + + return stio; +} + +void stio_close (stio_t* stio) +{ + while (stio->dev.tail) + { + stio_killdev (stio, stio->dev.tail); + } + + close (stio->mux); + STIO_MMGR_FREE (stio->mmgr, stio); +} + + +stio_dev_t* stio_makedev (stio_t* stio, stio_size_t dev_size, stio_dev_mth_t* dev_mth, stio_dev_evcb_t* dev_evcb, void* make_ctx) +{ + stio_dev_t* dev; + + if (dev_size < STIO_SIZEOF(stio_dev_t)) + { + stio->errnum = STIO_EINVAL; + return STIO_NULL; + } + + dev = STIO_MMGR_ALLOC (stio->mmgr, dev_size); + if (!dev) + { + stio->errnum = STIO_ENOMEM; + return STIO_NULL; + } + + STIO_MEMSET (dev, 0, dev_size); + dev->stio = stio; + dev->mth = dev_mth; + dev->evcb = dev_evcb; + + /* call the callback function first */ + stio->errnum = STIO_ENOERR; + if (dev->mth->make (dev, make_ctx) <= -1) + { + if (stio->errnum == STIO_ENOERR) stio->errnum = STIO_EDEVMAKE; + goto oops; + } + + /* ------------------------------------ */ + { + #if defined(_WIN32) + if (CreateIoCompletionPort ((HANDLE)dev->mth->getsyshnd(dev), stio->iocp, STIO_IOCP_KEY, 0) == NULL) + { + /* TODO: set errnum from GetLastError()... */ + goto oops_after_make; + } + + #else + if (stio_dev_event (dev, STIO_DEV_EVENT_ADD, STIO_DEV_EVENT_IN) <= -1) goto oops_after_make; + #endif + } + /* ------------------------------------ */ + + /* and place the new dev at the back */ + if (stio->dev.tail) stio->dev.tail->next = dev; + else stio->dev.head = dev; + dev->prev = stio->dev.tail; + stio->dev.tail = dev; + + return dev; + +oops_after_make: + dev->mth->kill (dev); +oops: + STIO_MMGR_FREE (stio->mmgr, dev); + return STIO_NULL; +} + +void stio_killdev (stio_t* stio, stio_dev_t* dev) +{ + STIO_ASSERT (stio == dev->stio); + + /* delink the dev object first */ + if (dev->prev) + dev->prev->next = dev->next; + else + stio->dev.head = dev->next; + + if (dev->next) + dev->next->prev = dev->prev; + else + stio->dev.tail = dev->prev; + + /* ------------------------------------ */ + { + #if defined(_WIN32) + /* nothing - can't deassociate it. closing the socket + * will do. kill should close it */ + #else + struct epoll_event ev; /* dummy */ + epoll_ctl (stio->mux, EPOLL_CTL_DEL, dev->mth->getsyshnd(dev), &ev); + /* don't care about failure */ + #endif + } + /* ------------------------------------ */ + + /* and call the callback function */ + dev->mth->kill (dev); + + STIO_MMGR_FREE (stio->mmgr, dev); +} + +int stio_prologue (stio_t* stio) +{ + + return 0; +} + +void stio_epilogue (stio_t* stio) +{ +} + +int stio_exec (stio_t* stio) +{ + int timeout; + +#if defined(_WIN32) + ULONG nentries, i; +#else + int nentries, i; +#endif + + /*if (!stio->dev.head) return 0;*/ + + timeout = 1000; //TODO: get_timeout (stio); // use the task heap to get the timeout. + +#if defined(_WIN32) +/* + if (GetQueuedCompletionStatusEx (stio->iocp, stio->ovls, STIO_COUNTOF(stio->ovls), &nentries, timeout, FALSE) == FALSE) + { + // TODO: set errnum + return -1; + } + + for (i = 0; i < nentries; i++) + { + } +*/ + +#else + + + + nentries = epoll_wait (stio->mux, stio->revs, STIO_COUNTOF(stio->revs), timeout); + if (nentries <= -1) + { + /* TODO: set errnum */ + return -1; + } + + for (i = 0; i < nentries; i++) + { + stio_dev_t* dev = stio->revs[i].data.ptr; + + if (dev->evcb->ready) + { + int x, events = 0; + + if (stio->revs[i].events & EPOLLERR) events |= STIO_DEV_EVENT_ERR; + if (stio->revs[i].events & (EPOLLHUP | EPOLLRDHUP)) events |= STIO_DEV_EVENT_HUP; + if (stio->revs[i].events & EPOLLIN) events |= STIO_DEV_EVENT_IN; + if (stio->revs[i].events & EPOLLOUT) events |= STIO_DEV_EVENT_OUT; + if (stio->revs[i].events & EPOLLPRI) events |= STIO_DEV_EVENT_PRI; + + /* return value of ready() + * <= -1 - failure. kill the device. + * == 0 - ok. but don't invoke recv() or send(). if you want to kill the device within the ready callback, return 0. + * >= 1 - everything is ok. */ +/* TODO: can the revs array contain the same file descriptor again??? */ + if ((x = dev->evcb->ready (dev, events)) <= -1) + stio_killdev (stio, dev); + else if (x >= 1) goto invoke_evcb; + } + else + { + invoke_evcb: + if (stio->revs[i].events & EPOLLPRI) + { + /* urgent data */ + printf ("has urgent data...\n"); + } + + if (stio->revs[i].events & EPOLLIN) + { + stio_len_t len; + int x; + + len = STIO_COUNTOF(stio->bigbuf); + x = dev->mth->recv (dev, stio->bigbuf, &len); + +printf ("DATA...recv %d length %d\n", (int)x, len); + if (x <= -1) + { + } + else if (x == 0) + { + stio_killdev (stio, dev); + } + else if (x >= 1) + { + /* data available??? */ + dev->evcb->on_recv (dev, stio->bigbuf, len); + } + } + + if (stio->revs[i].events & EPOLLOUT) + { + /* + if (there is data to write) + { + x = dev->mth->send (dev, stio->bigbuf, &len); + if (x <= -1) + { + } + + dev->evcb->on_sent (dv, stio->bigbuf, x); + }*/ + } + } + } + +#endif + + return 0; +} + +void stio_stop (stio_t* stio) +{ + stio->stopreq = 1; +} + +int stio_loop (stio_t* stio) +{ + if (!stio->dev.head) return 0; + + stio->stopreq = 0; + if (stio_prologue (stio) <= -1) return -1; + + while (!stio->stopreq && stio->dev.head) + { +printf ("executing stio_exec...%p \n", stio->dev.head); + if (stio_exec (stio) <= -1) break; + /* you can do other things here */ + } + + stio_epilogue (stio); + return 0; +} + + +int stio_dev_ioctl (stio_dev_t* dev, int cmd, void* arg) +{ + if (dev->mth->ioctl) return dev->mth->ioctl (dev, cmd, arg); + dev->stio->errnum = STIO_ENOSUP; /* TODO: different error code ? */ + return -1; +} + +int stio_dev_event (stio_dev_t* dev, stio_dev_event_cmd_t cmd, int flags) +{ +#if defined(_WIN32) + +#else + struct epoll_event ev; + int epoll_op; + + ev.events = EPOLLHUP | EPOLLERR; +#if defined(EPOLLRDHUP) + ev.events |= EPOLLRDHUP; +#endif + if (flags & STIO_DEV_EVENT_IN) ev.events |= EPOLLIN; + if (flags & STIO_DEV_EVENT_OUT) ev.events |= EPOLLOUT; + if (flags & STIO_DEV_EVENT_PRI) ev.events |= EPOLLPRI; + ev.data.ptr = dev; + + switch (cmd) + { + case STIO_DEV_EVENT_ADD: + epoll_op = EPOLL_CTL_ADD; + break; + + case STIO_DEV_EVENT_MOD: + epoll_op = EPOLL_CTL_MOD; + break; + + case STIO_DEV_EVENT_DEL: + epoll_op = EPOLL_CTL_ADD; + break; + + default: + dev->stio->errnum = STIO_EINVAL; + return -1; + } + + if (epoll_ctl (dev->stio->mux, epoll_op, dev->mth->getsyshnd(dev), &ev) == -1) + { + /* TODO: set dev->stio->errnum from errno */ + return -1; + } + + return 0; +#endif +} diff --git a/stio/lib/stio.h b/stio/lib/stio.h new file mode 100644 index 0000000..cf3c24f --- /dev/null +++ b/stio/lib/stio.h @@ -0,0 +1,291 @@ +#ifndef _STIO_H_ +#define _STIO_H_ + +/* TODO: remove these headers */ +#include +#include +#include +#include + + +typedef signed char stio_int8_t; +typedef unsigned char stio_uint8_t; +typedef unsigned long stio_size_t; +#define STIO_MEMSET(dst,byte,count) memset(dst,byte,count) +#define STIO_ASSERT assert + + +#define STIO_SIZEOF(x) sizeof(x) +#define STIO_COUNTOF(x) (sizeof(x) / sizeof(x[0])) +#define STIO_NULL ((void*)0) + +typedef struct stio_mmgr_t stio_mmgr_t; + +typedef void* (*stio_mmgr_alloc_t) (stio_mmgr_t* mmgr, stio_size_t size); +typedef void (*stio_mmgr_free_t) (stio_mmgr_t* mmgr, void* ptr); + +struct stio_mmgr_t +{ + stio_mmgr_alloc_t alloc; + stio_mmgr_free_t free; + void* ctx; +}; + +#define STIO_MMGR_ALLOC(mmgr,size) (mmgr)->alloc(mmgr,size) +#define STIO_MMGR_FREE(mmgr,ptr) (mmgr)->free(mmgr,ptr) + +struct stio_sckadr_t +{ + int family; + stio_uint8_t data[64]; /* TODO: use the actual sockaddr size */ +}; + +typedef struct stio_sckadr_t stio_sckadr_t; + +#if defined(_WIN32) +# define STIO_IOCP_KEY 1 + + typedef int stio_scklen_t; + typedef SOCKET stio_sckhnd_t; +# define STIO_SCKHND_INVALID (INVALID_SOCKET) + + typedef HANDLE stio_syshnd_t; +#else + typedef socklen_t stio_scklen_t; + typedef int stio_sckhnd_t; +# define STIO_SCKHND_INVALID (-1) + + typedef int stio_syshnd_t; +#endif + + + +/* ------------------------------------------------------------------------- */ +typedef struct stio_t stio_t; +typedef struct stio_dev_t stio_dev_t; +typedef struct stio_dev_mth_t stio_dev_mth_t; +typedef struct stio_dev_evcb_t stio_dev_evcb_t; +typedef unsigned int stio_len_t; /* TODO: remove it? */ + + +enum stio_errnum_t +{ + STIO_ENOERR, + STIO_ENOMEM, + STIO_EINVAL, + STIO_ENOSUP, /* not supported */ + + STIO_EDEVMAKE +}; + +typedef enum stio_errnum_t stio_errnum_t; + + +struct stio_dev_mth_t +{ + /* --------------------------------------------------------------------------------------------- */ + int (*make) (stio_dev_t* dev, void* ctx); /* mandatory. called in stix_makedev() */ + void (*kill) (stio_dev_t* dev); /* mandatory. called in stix_killdev(). called in stix_makedev() upon failure after make() success */ + stio_syshnd_t (*getsyshnd) (stio_dev_t* dev); /* mandatory. called in stix_makedev() after successful make() */ + + + int (*ioctl) (stio_dev_t* dev, int cmd, void* arg); + + /* --------------------------------------------------------------------------------------------- */ + int (*recv) (stio_dev_t* dev, void* data, stio_len_t* len); + /* --------------------------------------------------------------------------------------------- */ + int (*send) (stio_dev_t* dev, const void* data, stio_len_t* len); + /* --------------------------------------------------------------------------------------------- */ +}; + +struct stio_dev_evcb_t +{ + int (*ready) (stio_dev_t* dev, int events); + /*int (*on_error) (stio_dev_t* dev); + int (*on_hangup) (stio_dev_t* dev);*/ + int (*on_recv) (stio_dev_t* dev, const void* data, stio_len_t len); + int (*on_sent) (stio_dev_t* dev, const void* data, stio_len_t len); + +}; + +#define STIO_DEV_HEADERS \ + stio_t* stio; \ + stio_dev_mth_t* mth; \ + stio_dev_evcb_t* evcb; \ + stio_dev_t* prev; \ + stio_dev_t* next + +struct stio_dev_t +{ + STIO_DEV_HEADERS; +}; + +enum stio_dev_event_cmd_t +{ + STIO_DEV_EVENT_ADD, + STIO_DEV_EVENT_MOD, + STIO_DEV_EVENT_DEL +}; +typedef enum stio_dev_event_cmd_t stio_dev_event_cmd_t; + +enum stio_dev_event_flag_t +{ + STIO_DEV_EVENT_IN = (1 << 0), + STIO_DEV_EVENT_OUT = (1 << 1), + STIO_DEV_EVENT_PRI = (1 << 2), + STIO_DEV_EVENT_HUP = (1 << 3), + STIO_DEV_EVENT_ERR = (1 << 4) + +}; +typedef enum stio_dev_event_flag_t stio_dev_event_flag_t; + + + +/* -------------------------------------------------------------------------- */ + + +enum stio_dev_tcp_ioctl_cmd_t +{ + STIO_DEV_TCP_BIND, + STIO_DEV_TCP_CONNECT, + STIO_DEV_TCP_LISTEN +}; +typedef enum stio_dev_tcp_ioctl_cmd_t stio_dev_tcp_ioctl_cmd_t; + +enum stio_dev_tcp_state_t +{ + STIO_DEV_TCP_CONNECTING = (1 << 0), + STIO_DEV_TCP_CONNECTED = (1 << 1), + STIO_DEV_TCP_LISTENING = (1 << 2), + STIO_DEV_TCP_ACCEPTED = (1 << 3) +}; +typedef enum stio_dev_tcp_state_t stio_dev_tcp_state_t; + +typedef struct stio_dev_tcp_t stio_dev_tcp_t; + +typedef void (*stio_dev_tcp_on_connected_t) (stio_dev_tcp_t* dev); +typedef void (*stio_dev_tcp_on_accepted_t) (stio_dev_tcp_t* dev, stio_dev_tcp_t* clidev); +typedef void (*stio_dev_tcp_on_disconnected_t) (stio_dev_tcp_t* dev); + +struct stio_dev_tcp_t +{ + STIO_DEV_HEADERS; + + stio_sckhnd_t sck; + + unsigned int state; + + /* peer address - valid if one of the followings is set: + * STIO_DEV_TCP_ACCEPTED + * STIO_DEV_TCP_CONNECTED + * STIO_DEV_TCP_CONNECTING */ + stio_sckadr_t peer; + + /* parent tcp device. valid if STIO_DEV_TCP_ACCEPTED is set */ + stio_dev_tcp_t* parent; + + stio_dev_tcp_on_connected_t on_connected; + stio_dev_tcp_on_disconnected_t on_disconnected; + stio_dev_tcp_on_accepted_t on_accepted; +}; + +typedef struct stio_dev_tcp_bind_t stio_dev_tcp_bind_t; +struct stio_dev_tcp_bind_t +{ + int options; /* TODO: REUSEADDR , TRANSPARENT, etc or someting?? */ + stio_sckadr_t addr; + /* TODO: add device name for BIND_TO_DEVICE */ +}; + +typedef struct stio_dev_tcp_connect_t stio_dev_tcp_connect_t; +struct stio_dev_tcp_connect_t +{ + stio_sckadr_t addr; + stio_dev_tcp_on_connected_t on_connected; + stio_dev_tcp_on_disconnected_t on_disconnected; +}; + +typedef struct stio_dev_tcp_listen_t stio_dev_tcp_listen_t; +struct stio_dev_tcp_listen_t +{ + int backlogs; + stio_dev_tcp_on_accepted_t on_accepted; /* optional, but new connections are dropped immediately without this */ + stio_dev_tcp_on_disconnected_t on_disconnected; +}; + +typedef struct stio_dev_tcp_accept_t stio_dev_tcp_accept_t; +struct stio_dev_tcp_accept_t +{ + stio_syshnd_t sck; + stio_dev_tcp_t* parent; + stio_sckadr_t peer; +}; + +/* -------------------------------------------------------------------------- */ + +typedef struct stio_dev_udp_t stio_dev_udp_t; + +struct stio_dev_udp_t +{ + STIO_DEV_HEADERS; + stio_sckhnd_t sck; + + stio_sckadr_t peer; +}; + +/* -------------------------------------------------------------------------- */ + + +#ifdef __cplusplus +extern "C" { +#endif + +stio_t* stio_open ( + stio_mmgr_t* mmgr, + stio_size_t xtnsize, + stio_errnum_t* errnum +); + +void stio_close ( + stio_t* stio +); + +stio_dev_t* stio_makedev ( + stio_t* stio, + stio_size_t dev_size, + stio_dev_mth_t* dev_mth, + stio_dev_evcb_t* dev_evcb, + void* make_ctx +); + +void stio_killdev ( + stio_t* stio, + stio_dev_t* dev +); + + +stio_dev_tcp_t* stio_dev_tcp_make ( + stio_t* stio, + stio_sckadr_t* addr +); + +void stio_dev_tcp_kill ( + stio_dev_tcp_t* tcp +); + + +stio_dev_udp_t* stio_dev_udp_make ( + stio_t* stio, + stio_sckadr_t* addr +); + +void stio_dev_udp_kill ( + stio_dev_udp_t* udp +); + +#ifdef __cplusplus +} +#endif + + +#endif