X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=chiark-tcl.git;a=blobdiff_plain;f=dgram%2Fdgram.c;h=24c5446c0b1625528f98fbb5ce4dceb664845f28;hp=7e98df224c4842facb5590cf6da01905693b1a23;hb=ceed4cf646a34245b3bc88089a2187ebf7a41f0f;hpb=29867267f21e03efc9e19362a8391a3d0dc31cba diff --git a/dgram/dgram.c b/dgram/dgram.c index 7e98df2..24c5446 100644 --- a/dgram/dgram.c +++ b/dgram/dgram.c @@ -9,126 +9,165 @@ * if script not supplied, cancel */ -#include "tables.h" -#include "hbytes.h" +#include "dgram.h" typedef struct DgramSocket { + int ix; /* first ! */ int fd; + Tcl_Interp *ip; + ScriptToInvoke script; + void *addr_buf, *msg_buf; + int addr_buflen, msg_buflen; } DgramSocket; -static int n_socks; -static DgramSocket *socks; - -static int sockfail(Tcl_Interp *ip, int fd, const char *m) { - int e; - e= errno; - close(fd); - return posixerr(ip,e,m); -} - -int do_dgram_socket_create(ClientData cd, Tcl_Interp *ip, - SockAddr_Value local, int *sock_r) { - int fd, al, r, sock; +int cht_do_dgramsocket_create(ClientData cd, Tcl_Interp *ip, + SockAddr_Value local, void **sock_r) { + int fd, al, r; + DgramSocket *sock; const struct sockaddr *sa; - for (sock=0; sock=0; sock++); - if (sock>=n_socks) { - n_socks += 2; - n_socks *= 2; - socks= (void*)Tcl_Realloc((void*)socks, n_socks*sizeof(*socks)); - while (socksa_family, SOCK_DGRAM, 0); - if (fd<0) return posixerr(ip,errno,"socket"); - r= bind(fd, sa, al); if (r) return sockfail(ip,fd,"bind"); - r= setnonblock(fd, 1); if (r) return sockfail(ip,fd,"setnonblock"); + if (fd<0) return cht_posixerr(ip,errno,"socket"); + r= bind(fd, sa, al); if (r) return cht_newfdposixerr(ip,fd,"bind"); + r= cht_setnonblock(fd, 1); if (r) return cht_newfdposixerr(ip,fd,"setnonblock"); + + sock= TALLOC(sizeof(DgramSocket)); + sock->ix= -1; + sock->fd= fd; + sock->addr_buflen= al+1; + sock->addr_buf= TALLOC(sock->addr_buflen); + sock->msg_buflen= 0; + sock->msg_buf= 0; + cht_scriptinv_init(&sock->script); - socks[sock].fd= fd; *sock_r= sock; return TCL_OK; } -int do_dgram_socket_transmit(ClientData cd, Tcl_Interp *ip, - int sock, HBytes_Value data, +int cht_do_dgramsocket_transmit(ClientData cd, Tcl_Interp *ip, + void *sock_v, HBytes_Value data, SockAddr_Value remote) { + DgramSocket *sock= sock_v; int l, r; - r= sendto(socks[sock].fd, - hbytes_data(&data), l=hbytes_len(&data), + r= sendto(sock->fd, + cht_hb_data(&data), l=cht_hb_len(&data), 0, - sockaddr_addr(&remote), sockaddr_len(&remote)); - if (r==-1) return posixerr(ip,errno,"sendto"); - else if (r!=l) return staticerr(ip,"sendto gave wrong answer"); - return TCL_OK; -} - -int do_dgram_socket_close(ClientData cd, Tcl_Interp *ip, int sock) { - close(socks[sock].fd); /* nothing useful to be done with errors */ - socks[sock].fd= -1; + cht_sockaddr_addr(&remote), cht_sockaddr_len(&remote)); + if (r==-1) return cht_posixerr(ip,errno,"sendto"); + else if (r!=l) return cht_staticerr(ip,"sendto gave wrong answer",0); return TCL_OK; } -/* Arg parsing */ +static void cancel(DgramSocket *sock) { + if (sock->script.script) { + cht_scriptinv_cancel(&sock->script); + Tcl_DeleteFileHandler(sock->fd); + } +} -int pat_sockid(Tcl_Interp *ip, Tcl_Obj *o, int *val) { - int rc, sock; +static void recv_call(ClientData sock_cd, int mask) { + DgramSocket *sock= (void*)sock_cd; + Tcl_Interp *ip= sock->script.ipq; + int sz, rc, peek; + HBytes_Value message_val; + SockAddr_Value peer_val; + Tcl_Obj *args[3]; + struct msghdr mh; + struct iovec iov; + + cht_hb_empty(&message_val); + cht_sockaddr_clear(&peer_val); + + mh.msg_iov= &iov; + mh.msg_iovlen= 1; + mh.msg_control= 0; + mh.msg_controllen= 0; + mh.msg_flags= 0; + + peek= MSG_PEEK; - rc= Tcl_ConvertToType(ip,o,&sockid_type); - if (rc) return rc; + for (;;) { + mh.msg_name= sock->addr_buf; + mh.msg_namelen= sock->addr_buflen; + + iov.iov_base= sock->msg_buf; + iov.iov_len= sock->msg_buflen; + + sz= recvmsg(sock->fd, &mh, peek); + if (sz==-1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) rc=0; + else rc= cht_posixerr(ip,errno,"recvmsg"); + goto x_rc; + } + + assert(mh.msg_namelen < sock->addr_buflen); + + if (!(mh.msg_flags & MSG_TRUNC)) { + if (!peek) break; + peek= 0; + continue; + } + + TFREE(sock->msg_buf); + assert(sock->msg_buflen < INT_MAX/4); + sock->msg_buflen *= 2; + sock->msg_buflen += 100; + sock->msg_buf= TALLOC(sock->msg_buflen); + } - sock= o->internalRep.longValue; - if (sock >= n_socks || socks[sock].fd==-1) - return staticerr(ip,"dgram socket not open"); + cht_hb_array(&message_val, iov.iov_base, sz); + cht_sockaddr_create(&peer_val, mh.msg_name, mh.msg_namelen); - *val= sock; - return TCL_OK; -} - -Tcl_Obj *ret_sockid(Tcl_Interp *ip, int val) { - Tcl_Obj *o; + args[0]= cht_ret_hb(ip, message_val); cht_hb_empty(&message_val); + args[1]= cht_ret_sockaddr(ip, peer_val); cht_sockaddr_clear(&peer_val); + args[2]= cht_ret_iddata(ip, sock, &cht_dgram_socks); + cht_scriptinv_invoke(&sock->script,3,args); - o= Tcl_NewObj(); - Tcl_InvalidateStringRep(o); - o->internalRep.longValue= val; - o->typePtr= &sockid_type; - return o; + rc= 0; + +x_rc: + if (rc) + Tcl_BackgroundError(ip); } -static void sockid_t_free(Tcl_Obj *o) { } - -static void sockid_t_dup(Tcl_Obj *src, Tcl_Obj *dup) { - dup->internalRep= src->internalRep; +int cht_do_dgramsocket_on_receive(ClientData cd, Tcl_Interp *ip, + void *sock_v, Tcl_Obj *newscript) { + DgramSocket *sock= sock_v; + int rc; + + cancel(sock); + + if (newscript) { + rc= cht_scriptinv_set(&sock->script, ip, newscript, 0); + if (rc) return rc; + } + + Tcl_CreateFileHandler(sock->fd, TCL_READABLE, recv_call, sock); + return TCL_OK; } -static void sockid_t_ustr(Tcl_Obj *o) { - char buf[100]; - int l; +static void destroy(DgramSocket *sock) { + cancel(sock); + close(sock->fd); /* nothing useful to be done with errors */ + TFREE(sock->addr_buf); + TFREE(sock->msg_buf); + TFREE(sock); +} - snprintf(buf,sizeof(buf),"dgramsock%d", (int)o->internalRep.longValue); - l= o->length= strlen(buf); - o->bytes= TALLOC(l+1); - strcpy(o->bytes, buf); +static void destroy_idtabcb(Tcl_Interp *ip, void *sock_v) { + destroy(sock_v); } -static int sockid_t_sfa(Tcl_Interp *ip, Tcl_Obj *o) { - unsigned long ul; - char *ep, *str; - - str= Tcl_GetStringFromObj(o,0); - if (memcmp(str,"dgramsock",9)) return staticerr(ip,"bad dgram socket id"); - errno=0; ul=strtoul(str+9,&ep,10); - if (errno || *ep) return staticerr(ip,"bad dgram socket id number"); - if (ul > INT_MAX) return staticerr(ip,"out of range dgram socket id"); - o->internalRep.longValue= ul; +int cht_do_dgramsocket_close(ClientData cd, Tcl_Interp *ip, void *sock_v) { + cht_tabledataid_disposing(ip,sock_v,&cht_dgram_socks); + destroy(sock_v); return TCL_OK; } -Tcl_ObjType sockid_type = { - "sockid-nearly", - sockid_t_free, sockid_t_dup, sockid_t_ustr, sockid_t_sfa +const IdDataSpec cht_dgram_socks= { + "dgramsock", "dgramsock-table", destroy_idtabcb };