From fe8bab1cc313d0049bbb839589c68059807518c8 Mon Sep 17 00:00:00 2001 From: Steve Bennett Date: Tue, 8 Nov 2011 17:56:11 +1000 Subject: [PATCH] aio: Add dgram unix socket support Also add support for sockname and peername And remove unix domain sockets when the socket is closed And generally clean up the socket support Signed-off-by: Steve Bennett --- examples/unix.client | 9 + examples/unix.dgram.client | 18 ++ examples/unix.dgram.server | 35 +++ examples/unix.server | 48 ++++ jim-aio.c | 677 +++++++++++++++++++++++++++------------------ 5 files changed, 512 insertions(+), 275 deletions(-) create mode 100644 examples/unix.client create mode 100644 examples/unix.dgram.client create mode 100644 examples/unix.dgram.server create mode 100644 examples/unix.server diff --git a/examples/unix.client b/examples/unix.client new file mode 100644 index 0000000..12a5bf8 --- /dev/null +++ b/examples/unix.client @@ -0,0 +1,9 @@ +# Example of sending via a connected tcp socket + +set s [socket unix unix.tmp] + +foreach i [range 1 20] { + $s puts "expr {1 << $i}" + + puts [$s gets] +} diff --git a/examples/unix.dgram.client b/examples/unix.dgram.client new file mode 100644 index 0000000..d9dd8ca --- /dev/null +++ b/examples/unix.dgram.client @@ -0,0 +1,18 @@ +# Example of sending from an unconnected socket + +# Now sending via a connected udp socket + +set s [socket unix.dgram unix.sock] +$s buffering none + +puts "dgram socket [$s sockname] connected to [$s peername]" + +foreach i [range 5 10] { + # Socket is connected, so can just use puts here + # No need to flush because we set 'buffering none' above. + $s puts -nonewline "$i * $i" + #$s flush + + # Receive the response - max length of 100 + puts [$s recvfrom 100] +} diff --git a/examples/unix.dgram.server b/examples/unix.dgram.server new file mode 100644 index 0000000..43ada19 --- /dev/null +++ b/examples/unix.dgram.server @@ -0,0 +1,35 @@ +# Example of a udp server which sends a response + +# Listen on port 20000. No host specified means 0.0.0.0 +file delete unix.sock +set s [socket unix.dgram.server unix.sock] + +puts "Listening on dgram socket [$s sockname]" + +set count 0 + +# For each request... +$s readable { + # Get the request (max 80 chars) - need the source address + set buf [$s recvfrom 80 addr] + + puts -nonewline "read '$buf' from client $addr" + + try { + set result "$buf = [expr $buf]" + } on error {msg} { + set result "Error: $buf => $msg" + } + + puts ", sending '$result' to client $addr" + + # Send the result back to where it came from + $s sendto $result $addr +} + +# Handle signals so the socket is removed on exit +signal handle SIGINT SIGTERM + +catch -signal { + vwait done +} diff --git a/examples/unix.server b/examples/unix.server new file mode 100644 index 0000000..fa52992 --- /dev/null +++ b/examples/unix.server @@ -0,0 +1,48 @@ +# Example of a unix domain stream server which sends a response + +file delete unix.tmp +set s [socket unix.server unix.tmp] + +puts "Listening on [$s sockname]" + +$s readable { + # Clean up children + wait -nohang 0 + set sock [$s accept] + + # Make this server forking so we can accept multiple + # simultaneous connections + if {[os.fork] == 0} { + # We don't want the unix domain path to be deleted here + $s close -nodelete + + $sock buffering line + + # Get the requests, one line at a time an evaluate + while {[$sock gets buf] >= 0} { + set buf [string trim $buf] + if {$buf in {? help}} { + set result "Enter any Tcl command to run in the server" + } else { + try { + set result [eval $buf] + set result [string map [list \\ \\\\ \n \\n \r \\r] $result] + } on error {msg} { + set result "Error: $buf => $msg" + } + } + + # Send the result back to where it came from + $sock puts $result + } + } + + $sock close +} + +# Handle signals so the socket is removed on exit +signal handle SIGINT SIGTERM + +catch -signal { + vwait done +} diff --git a/jim-aio.c b/jim-aio.c index ce37e3b..ba8216f 100644 --- a/jim-aio.c +++ b/jim-aio.c @@ -46,6 +46,7 @@ #include #include #include +#include #ifdef HAVE_UNISTD_H #include #include @@ -93,6 +94,7 @@ #endif #define AIO_KEEPOPEN 1 +#define AIO_NODELETE 2 #if defined(JIM_IPV6) #define IPV6 1 @@ -102,6 +104,11 @@ #define PF_INET6 0 #endif #endif +#if defined(HAVE_SYS_UN_H) && defined(PF_UNIX) +#define UNIX_SOCKETS 1 +#else +#define UNIX_SOCKETS 0 +#endif #ifdef JIM_ANSIC /* no fdopen() with ANSIC, so can't support these */ @@ -110,12 +117,16 @@ #endif #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP) +/* Avoid type punned pointers */ union sockaddr_any { struct sockaddr sa; struct sockaddr_in sin; #if IPV6 struct sockaddr_in6 sin6; #endif +#if UNIX_SOCKETS + struct sockaddr_un sun; +#endif }; #ifndef HAVE_INET_NTOP @@ -256,9 +267,9 @@ static const char *ssl_strerror(struct AioFile *af) if (err) { return ERR_error_string(err, NULL); } - else { - return stdio_strerror(af); - } + else { + return stdio_strerror(af); + } } static int ssl_verify(struct AioFile *af) @@ -290,10 +301,73 @@ static const JimAioFopsType ssl_fops = { static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv); static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename, - const char *hdlfmt, int family, const char *mode); + const char *hdlfmt, int family, const char *mode, int flags); #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP) -static int JimParseIPv6Address(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, int *salen) +#ifndef HAVE_GETADDRINFO +/* + * Poor man's getaddrinfo(). + * hints->ai_family must be set and must be PF_INET or PF_INET6 + * Only returns the first matching result. + * servname must be numeric. + */ +struct addrinfo { + int ai_family; + socklen_t ai_addrlen; + struct sockaddr *ai_addr; /* simply points to ai_storage */ + union sockaddr_any ai_storage; +}; + +static int getaddrinfo(const char *hostname, const char *servname, + const struct addrinfo *hints, struct addrinfo **res) +{ + struct hostent *he; + char *end; + unsigned long port = strtoul(servname, &end, 10); + if (port == 0 || port > 65536 || *end) { + errno = EINVAL; + return -1; + } + + if ((he = gethostbyname(hostname)) != NULL) { + int i; + for (i = 0; he->h_addr_list[i]; i++) { + if (he->h_addrtype == hints->ai_family) { + struct addrinfo *ai = malloc(sizeof(*ai)); + memset(ai, 0, sizeof(*ai)); + ai->ai_family = he->h_addrtype; + ai->ai_addr = &ai->ai_storage.sa; + if (ai->ai_family == PF_INET) { + ai->ai_addrlen = sizeof(ai->ai_storage.sin); + ai->ai_storage.sin.sin_family = he->h_addrtype; + assert(sizeof(ai->ai_storage.sin.sin_addr) == he->h_length); + memcpy(&ai->ai_storage.sin.sin_addr, he->h_addr_list[i], he->h_length); + ai->ai_storage.sin.sin_port = htons(port); + } +#if IPV6 + else { + ai->ai_addrlen = sizeof(ai->ai_storage.sin6); + ai->ai_storage.sin6.sin6_family = he->h_addrtype; + assert(sizeof(ai->ai_storage.sin6.sin6_addr) == he->h_length); + memcpy(&ai->ai_storage.sin6.sin6_addr, he->h_addr_list[i], he->h_length); + ai->ai_storage.sin6.sin6_port = htons(port); + } +#endif + *res = ai; + return 0; + } + } + } + return ENOENT; +} + +static void freeaddrinfo(struct addrinfo *ai) +{ + free(ai); +} +#endif + +static int JimParseIPv6Address(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, socklen_t *salen) { #if IPV6 /* @@ -338,16 +412,13 @@ static int JimParseIPv6Address(Jim_Interp *interp, const char *hostport, union s memset(&req, '\0', sizeof(req)); req.ai_family = PF_INET6; - if (getaddrinfo(sthost, NULL, &req, &ai)) { + if (getaddrinfo(sthost, stport, &req, &ai)) { Jim_SetResultFormatted(interp, "Not a valid address: %s", hostport); ret = JIM_ERR; } else { - memcpy(&sa->sin, ai->ai_addr, ai->ai_addrlen); + memcpy(&sa->sin6, ai->ai_addr, ai->ai_addrlen); *salen = ai->ai_addrlen; - - sa->sin.sin_port = htons(atoi(stport)); - freeaddrinfo(ai); } Jim_Free(sthost); @@ -359,7 +430,7 @@ static int JimParseIPv6Address(Jim_Interp *interp, const char *hostport, union s #endif } -static int JimParseIpAddress(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, int *salen) +static int JimParseIpAddress(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, socklen_t *salen) { /* An IPv4 addr/port looks like: * 192.168.1.5 @@ -372,6 +443,8 @@ static int JimParseIpAddress(Jim_Interp *interp, const char *hostport, union soc char *sthost = NULL; const char *stport; int ret = JIM_OK; + struct addrinfo req; + struct addrinfo *ai; stport = strrchr(hostport, ':'); if (!stport) { @@ -384,37 +457,16 @@ static int JimParseIpAddress(Jim_Interp *interp, const char *hostport, union soc stport++; } - { -#ifdef HAVE_GETADDRINFO - struct addrinfo req; - struct addrinfo *ai; - memset(&req, '\0', sizeof(req)); - req.ai_family = PF_INET; - - if (getaddrinfo(sthost, NULL, &req, &ai)) { - ret = JIM_ERR; - } - else { - memcpy(&sa->sin, ai->ai_addr, ai->ai_addrlen); - *salen = ai->ai_addrlen; - freeaddrinfo(ai); - } -#else - struct hostent *he; + memset(&req, '\0', sizeof(req)); + req.ai_family = PF_INET; + if (getaddrinfo(sthost, stport, &req, &ai)) { ret = JIM_ERR; - - if ((he = gethostbyname(sthost)) != NULL) { - if (he->h_length == sizeof(sa->sin.sin_addr)) { - *salen = sizeof(sa->sin); - sa->sin.sin_family= he->h_addrtype; - memcpy(&sa->sin.sin_addr, he->h_addr, he->h_length); /* set address */ - ret = JIM_OK; - } - } -#endif - - sa->sin.sin_port = htons(atoi(stport)); + } + else { + memcpy(&sa->sin, ai->ai_addr, ai->ai_addrlen); + *salen = ai->ai_addrlen; + freeaddrinfo(ai); } Jim_Free(sthost); @@ -425,46 +477,97 @@ static int JimParseIpAddress(Jim_Interp *interp, const char *hostport, union soc return ret; } -#ifdef HAVE_SYS_UN_H -static int JimParseDomainAddress(Jim_Interp *interp, const char *path, struct sockaddr_un *sa) +#if UNIX_SOCKETS +static int JimParseDomainAddress(Jim_Interp *interp, const char *path, union sockaddr_any *sa, socklen_t *salen) { - sa->sun_family = PF_UNIX; - snprintf(sa->sun_path, sizeof(sa->sun_path), "%s", path); + sa->sun.sun_family = PF_UNIX; + snprintf(sa->sun.sun_path, sizeof(sa->sun.sun_path), "%s", path); + *salen = strlen(sa->sun.sun_path) + 1 + sizeof(sa->sun.sun_family); return JIM_OK; } #endif +static int JimParseSocketAddress(Jim_Interp *interp, int family, const char *addr, union sockaddr_any *sa, socklen_t *salen) +{ + switch (family) { +#if UNIX_SOCKETS + case PF_UNIX: + return JimParseDomainAddress(interp, addr, sa, salen); +#endif + case PF_INET6: + return JimParseIPv6Address(interp, addr, sa, salen); + case PF_INET: + return JimParseIpAddress(interp, addr, sa, salen); + } + return JIM_ERR; +} + /** - * Format that address in 'sa' as a string and store in variable 'varObjPtr' + * Format that address in 'sa' as a string and return it as a zero-refcount object. + * */ -static int JimFormatIpAddress(Jim_Interp *interp, Jim_Obj *varObjPtr, const union sockaddr_any *sa) +static Jim_Obj *JimFormatSocketAddress(Jim_Interp *interp, const union sockaddr_any *sa, socklen_t salen) { /* INET6_ADDRSTRLEN is 46. Add some for [] and port */ char addrbuf[60]; - + const char *addr = addrbuf; + int addrlen = -1; + + switch (sa->sa.sa_family) { +#if UNIX_SOCKETS + case PF_UNIX: + addr = sa->sun.sun_path; + addrlen = salen - 1 - sizeof(sa->sun.sun_family); + if (addrlen < 0) { + addrlen = 0; + } + break; +#endif #if IPV6 - if (sa->sa.sa_family == PF_INET6) { - addrbuf[0] = '['; - /* Allow 9 for []:65535\0 */ - inet_ntop(sa->sa.sa_family, &sa->sin6.sin6_addr, addrbuf + 1, sizeof(addrbuf) - 9); - snprintf(addrbuf + strlen(addrbuf), 8, "]:%d", ntohs(sa->sin.sin_port)); - } - else + case PF_INET6: + addrbuf[0] = '['; + /* Allow 9 for []:65535\0 */ + inet_ntop(sa->sa.sa_family, &sa->sin6.sin6_addr, addrbuf + 1, sizeof(addrbuf) - 9); + snprintf(addrbuf + strlen(addrbuf), 8, "]:%d", ntohs(sa->sin6.sin6_port)); + break; #endif - if (sa->sa.sa_family == PF_INET) { - /* Allow 7 for :65535\0 */ - inet_ntop(sa->sa.sa_family, &sa->sin.sin_addr, addrbuf, sizeof(addrbuf) - 7); - snprintf(addrbuf + strlen(addrbuf), 7, ":%d", ntohs(sa->sin.sin_port)); - } - else { - /* recvfrom still works on unix domain sockets, etc */ - addrbuf[0] = 0; + case PF_INET: + /* Allow 7 for :65535\0 */ + inet_ntop(sa->sa.sa_family, &sa->sin.sin_addr, addrbuf, sizeof(addrbuf) - 7); + snprintf(addrbuf + strlen(addrbuf), 7, ":%d", ntohs(sa->sin.sin_port)); + break; + + default: + /* Otherwise just an empty address */ + addr = ""; + fprintf(stderr, "%s:%d", __FILE__, __LINE__); + break; } - return Jim_SetVariable(interp, varObjPtr, Jim_NewStringObj(interp, addrbuf, -1)); + return Jim_NewStringObj(interp, addr, addrlen); +} + +static int JimSetVariableSocketAddress(Jim_Interp *interp, Jim_Obj *varObjPtr, const union sockaddr_any *sa, socklen_t salen) +{ + int ret; + Jim_Obj *objPtr = JimFormatSocketAddress(interp, sa, salen); + Jim_IncrRefCount(objPtr); + ret = Jim_SetVariable(interp, varObjPtr, objPtr); + Jim_DecrRefCount(interp, objPtr); + return ret; } +static Jim_Obj *aio_sockname(Jim_Interp *interp, AioFile *af) +{ + union sockaddr_any sa; + socklen_t salen = sizeof(sa); + + if (getsockname(af->fd, &sa.sa, &salen) < 0) { + return NULL; + } + return JimFormatSocketAddress(interp, &sa, salen); +} #endif /* JIM_BOOTSTRAP */ static const char *JimAioErrorString(AioFile *af) @@ -489,11 +592,11 @@ static void JimAioSetError(Jim_Interp *interp, Jim_Obj *name) static int JimCheckStreamError(Jim_Interp *interp, AioFile *af) { - int ret = af->fops->error(af); - if (ret) { - JimAioSetError(interp, af->filename); - } - return ret; + int ret = af->fops->error(af); + if (ret) { + JimAioSetError(interp, af->filename); + } + return ret; } static void JimAioDelProc(Jim_Interp *interp, void *privData) @@ -502,6 +605,19 @@ static void JimAioDelProc(Jim_Interp *interp, void *privData) JIM_NOTUSED(interp); +#if UNIX_SOCKETS + if (af->addr_family == PF_UNIX && (af->openFlags & AIO_NODELETE) == 0) { + /* If this is bound, delete the socket file now */ + Jim_Obj *filenameObj = aio_sockname(interp, af); + if (filenameObj) { + if (Jim_Length(filenameObj)) { + remove(Jim_String(filenameObj)); + } + Jim_FreeNewObj(interp, filenameObj); + } + } +#endif + Jim_DecrRefCount(interp, af->filename); #ifdef jim_ext_eventloop @@ -779,7 +895,7 @@ static int aio_cmd_recvfrom(Jim_Interp *interp, int argc, Jim_Obj *const *argv) Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, rlen)); if (argc > 1) { - return JimFormatIpAddress(interp, argv[1], &sa); + return JimSetVariableSocketAddress(interp, argv[1], &sa, salen); } return JIM_OK; @@ -794,14 +910,9 @@ static int aio_cmd_sendto(Jim_Interp *interp, int argc, Jim_Obj *const *argv) const char *wdata; union sockaddr_any sa; const char *addr = Jim_String(argv[1]); - int salen; + socklen_t salen; - if (IPV6 && af->addr_family == PF_INET6) { - if (JimParseIPv6Address(interp, addr, &sa, &salen) != JIM_OK) { - return JIM_ERR; - } - } - else if (JimParseIpAddress(interp, addr, &sa, &salen) != JIM_OK) { + if (JimParseSocketAddress(interp, af->addr_family, addr, &sa, &salen) != JIM_OK) { return JIM_ERR; } wdata = Jim_GetString(argv[0], &wlen); @@ -821,23 +932,50 @@ static int aio_cmd_accept(Jim_Interp *interp, int argc, Jim_Obj *const *argv) AioFile *af = Jim_CmdPrivData(interp); int sock; union sockaddr_any sa; - socklen_t addrlen = sizeof(sa); + socklen_t salen = sizeof(sa); - sock = accept(af->fd, &sa.sa, &addrlen); + sock = accept(af->fd, &sa.sa, &salen); if (sock < 0) { JimAioSetError(interp, NULL); return JIM_ERR; } if (argc > 0) { - if (JimFormatIpAddress(interp, argv[0], &sa) != JIM_OK) { + if (JimSetVariableSocketAddress(interp, argv[0], &sa, salen) != JIM_OK) { return JIM_ERR; } } /* Create the file command */ return JimMakeChannel(interp, NULL, sock, Jim_NewStringObj(interp, "accept", -1), - "aio.sockstream%ld", af->addr_family, "r+") ? JIM_OK : JIM_ERR; + "aio.sockstream%ld", af->addr_family, "r+", AIO_NODELETE) ? JIM_OK : JIM_ERR; +} + +static int aio_cmd_sockname(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + AioFile *af = Jim_CmdPrivData(interp); + Jim_Obj *objPtr = aio_sockname(interp, af); + + if (objPtr == NULL) { + JimAioSetError(interp, NULL); + return JIM_ERR; + } + Jim_SetResult(interp, objPtr); + return JIM_OK; +} + +static int aio_cmd_peername(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + AioFile *af = Jim_CmdPrivData(interp); + union sockaddr_any sa; + socklen_t salen = sizeof(sa); + + if (getpeername(af->fd, &sa.sa, &salen) < 0) { + JimAioSetError(interp, NULL); + return JIM_ERR; + } + Jim_SetResult(interp, JimFormatSocketAddress(interp, &sa, salen)); + return JIM_OK; } static int aio_cmd_listen(Jim_Interp *interp, int argc, Jim_Obj *const *argv) @@ -880,23 +1018,38 @@ static int aio_cmd_eof(Jim_Interp *interp, int argc, Jim_Obj *const *argv) static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { if (argc == 3) { -#if defined(HAVE_SOCKETS) && defined(HAVE_SHUTDOWN) - static const char * const options[] = { "r", "w", NULL }; - enum { OPT_R, OPT_W, }; - int option; + int option = -1; +#if defined(HAVE_SOCKETS) + static const char * const options[] = { "r", "w", "-nodelete", NULL }; + enum { OPT_R, OPT_W, OPT_NODELETE }; AioFile *af = Jim_CmdPrivData(interp); if (Jim_GetEnum(interp, argv[2], options, &option, NULL, JIM_ERRMSG) != JIM_OK) { return JIM_ERR; } - if (shutdown(af->fd, option == OPT_R ? SHUT_RD : SHUT_WR) == 0) { - return JIM_OK; - } - JimAioSetError(interp, NULL); -#else - Jim_SetResultString(interp, "async close not supported", -1); #endif - return JIM_ERR; + switch (option) { +#if defined(HAVE_SHUTDOWN) + case OPT_R: + case OPT_W: + if (shutdown(af->fd, option == OPT_R ? SHUT_RD : SHUT_WR) == 0) { + return JIM_OK; + } + JimAioSetError(interp, NULL); + return JIM_ERR; +#endif +#if UNIX_SOCKETS + case OPT_NODELETE: + if (af->addr_family == PF_UNIX) { + af->openFlags |= AIO_NODELETE; + break; + } + /* fall through */ +#endif + default: + Jim_SetResultString(interp, "not supported", -1); + return JIM_ERR; + } } return Jim_DeleteCommand(interp, Jim_String(argv[0])); @@ -1021,7 +1174,7 @@ static const struct sockopt_def { static int aio_cmd_sockopt(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { AioFile *af = Jim_CmdPrivData(interp); - int i; + size_t i; if (argc == 0) { Jim_Obj *dictObjPtr = Jim_NewListObj(interp, NULL, 0); @@ -1454,6 +1607,20 @@ static const jim_subcmd_type aio_command_table[] = { 2, /* Description: Return a dictionary of sockopts, or set the value of a sockopt */ }, + { "sockname", + NULL, + aio_cmd_sockname, + 0, + 0, + /* Description: Returns the local address of the socket, if any */ + }, + { "peername", + NULL, + aio_cmd_peername, + 0, + 0, + /* Description: Returns the remote address of the socket, if any */ + }, #endif /* JIM_BOOTSTRAP */ { "flush", NULL, @@ -1626,7 +1793,7 @@ static int JimAioOpenCommand(Jim_Interp *interp, int argc, } } #endif - return JimMakeChannel(interp, NULL, -1, argv[1], "aio.handle%ld", 0, mode) ? JIM_OK : JIM_ERR; + return JimMakeChannel(interp, NULL, -1, argv[1], "aio.handle%ld", 0, mode, 0) ? JIM_OK : JIM_ERR; } #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP) @@ -1672,17 +1839,10 @@ static SSL_CTX *JimAioSslCtx(Jim_Interp *interp) * Returns the AioFile pointer on sucess or NULL on failure. */ static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename, - const char *hdlfmt, int family, const char *mode) + const char *hdlfmt, int family, const char *mode, int flags) { AioFile *af; char buf[AIO_CMD_LEN]; - int openFlags = 0; - - snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp)); - - if (fh) { - openFlags = AIO_KEEPOPEN; - } snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp)); if (!filename) { @@ -1717,11 +1877,11 @@ static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *fi memset(af, 0, sizeof(*af)); af->fp = fh; af->filename = filename; - af->openFlags = openFlags; + af->openFlags = flags; #ifndef JIM_ANSIC af->fd = fileno(fh); #ifdef FD_CLOEXEC - if ((openFlags & AIO_KEEPOPEN) == 0) { + if ((flags & AIO_KEEPOPEN) == 0) { (void)fcntl(af->fd, F_SETFD, FD_CLOEXEC); } #endif @@ -1740,17 +1900,17 @@ static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *fi return af; } -#if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && defined(HAVE_SYS_UN_H)) +#if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && UNIX_SOCKETS) /** * Create a pair of channels. e.g. from pipe() or socketpair() */ static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename, const char *hdlfmt, int family, const char *mode[2]) { - if (JimMakeChannel(interp, NULL, p[0], filename, hdlfmt, family, mode[0])) { + if (JimMakeChannel(interp, NULL, p[0], filename, hdlfmt, family, mode[0], 0)) { Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0); Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp)); - if (JimMakeChannel(interp, NULL, p[1], filename, hdlfmt, family, mode[1])) { + if (JimMakeChannel(interp, NULL, p[1], filename, hdlfmt, family, mode[1], 0)) { Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp)); Jim_SetResult(interp, objPtr); return JIM_OK; @@ -1789,10 +1949,11 @@ static int JimAioPipeCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { - const char *hdlfmt = "aio.unknown%ld"; const char *socktypes[] = { "unix", "unix.server", + "unix.dgram", + "unix.dgram.server", "dgram", "dgram.server", "stream", @@ -1805,6 +1966,8 @@ static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { SOCK_UNIX, SOCK_UNIX_SERVER, + SOCK_UNIX_DGRAM, + SOCK_UNIX_DGRAM_SERVER, SOCK_DGRAM_CLIENT, SOCK_DGRAM_SERVER, SOCK_STREAM_CLIENT, @@ -1814,11 +1977,16 @@ static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) }; int socktype; int sock; - const char *hostportarg = NULL; - int res; + const char *addr = NULL; + const char *bind_addr = NULL; + const char *connect_addr = NULL; + union sockaddr_any sa; + socklen_t salen; int on = 1; - const char *mode = "r+"; + int reuse = 0; + int do_listen = 0; int family = PF_INET; + int type = SOCK_STREAM; Jim_Obj *argv0 = argv[0]; int ipv6 = 0; @@ -1844,193 +2012,113 @@ static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) Jim_SetEmptyResult(interp); - hdlfmt = "aio.sock%ld"; - if (argc > 2) { - hostportarg = Jim_String(argv[2]); + addr = Jim_String(argv[2]); } +#if defined(HAVE_SOCKETPAIR) && UNIX_SOCKETS + if (socktype == SOCK_STREAM_SOCKETPAIR) { + int p[2]; + static const char *mode[2] = { "r+", "r+" }; + + if (addr || ipv6) { + goto wrongargs; + } + + if (socketpair(PF_UNIX, SOCK_STREAM, 0, p) < 0) { + JimAioSetError(interp, NULL); + return JIM_ERR; + } + return JimMakeChannelPair(interp, p, argv[1], "aio.sockpair%ld", PF_UNIX, mode); + } +#endif + +#if defined(HAVE_PIPE) + if (socktype == SOCK_STREAM_PIPE) { + if (addr || ipv6) { + goto wrongargs; + } + return JimAioPipeCommand(interp, 1, &argv[1]); + } +#endif + + /* Now all these socket types are very similar */ switch (socktype) { case SOCK_DGRAM_CLIENT: - if (argc == 2) { - /* No address, so an unconnected dgram socket */ - sock = socket(family, SOCK_DGRAM, 0); - if (sock < 0) { - JimAioSetError(interp, NULL); - return JIM_ERR; - } - break; - } - /* fall through */ - case SOCK_STREAM_CLIENT: - { - union sockaddr_any sa; - int salen; - - if (argc != 3) { - goto wrongargs; - } + connect_addr = addr; + type = SOCK_DGRAM; + break; - if (ipv6) { - if (JimParseIPv6Address(interp, hostportarg, &sa, &salen) != JIM_OK) { - return JIM_ERR; - } - } - else if (JimParseIpAddress(interp, hostportarg, &sa, &salen) != JIM_OK) { - return JIM_ERR; - } - sock = socket(family, (socktype == SOCK_DGRAM_CLIENT) ? SOCK_DGRAM : SOCK_STREAM, 0); - if (sock < 0) { - JimAioSetError(interp, NULL); - return JIM_ERR; - } - res = connect(sock, &sa.sa, salen); - if (res) { - JimAioSetError(interp, argv[2]); - close(sock); - return JIM_ERR; - } + case SOCK_STREAM_CLIENT: + if (addr == NULL) { + goto wrongargs; } + connect_addr = addr; break; case SOCK_STREAM_SERVER: - case SOCK_DGRAM_SERVER: - { - union sockaddr_any sa; - int salen; - - if (argc != 3) { - goto wrongargs; - } - - if (ipv6) { - if (JimParseIPv6Address(interp, hostportarg, &sa, &salen) != JIM_OK) { - return JIM_ERR; - } - } - else if (JimParseIpAddress(interp, hostportarg, &sa, &salen) != JIM_OK) { - return JIM_ERR; - } - sock = socket(family, (socktype == SOCK_DGRAM_SERVER) ? SOCK_DGRAM : SOCK_STREAM, 0); - if (sock < 0) { - JimAioSetError(interp, NULL); - return JIM_ERR; - } - - /* Enable address reuse */ - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); + if (addr == NULL) { + goto wrongargs; + } + bind_addr = addr; + reuse = 1; + do_listen = 1; + break; - res = bind(sock, &sa.sa, salen); - if (res) { - JimAioSetError(interp, argv[2]); - close(sock); - return JIM_ERR; - } - if (socktype == SOCK_STREAM_SERVER) { - res = listen(sock, 5); - if (res) { - JimAioSetError(interp, NULL); - close(sock); - return JIM_ERR; - } - } - hdlfmt = "aio.socksrv%ld"; + case SOCK_DGRAM_SERVER: + if (addr == NULL) { + goto wrongargs; } + bind_addr = addr; + type = SOCK_DGRAM; + reuse = 1; break; -#ifdef HAVE_SYS_UN_H +#if UNIX_SOCKETS case SOCK_UNIX: - { - struct sockaddr_un sa; - socklen_t len; - - if (argc != 3 || ipv6) { - goto wrongargs; - } - - if (JimParseDomainAddress(interp, hostportarg, &sa) != JIM_OK) { - JimAioSetError(interp, argv[2]); - return JIM_ERR; - } - family = PF_UNIX; - sock = socket(PF_UNIX, SOCK_STREAM, 0); - if (sock < 0) { - JimAioSetError(interp, NULL); - return JIM_ERR; - } - len = strlen(sa.sun_path) + 1 + sizeof(sa.sun_family); - res = connect(sock, (struct sockaddr *)&sa, len); - if (res) { - JimAioSetError(interp, argv[2]); - close(sock); - return JIM_ERR; - } - hdlfmt = "aio.sockunix%ld"; - break; + if (addr == NULL) { + goto wrongargs; } + connect_addr = addr; + family = PF_UNIX; + break; - case SOCK_UNIX_SERVER: - { - struct sockaddr_un sa; - socklen_t len; - - if (argc != 3 || ipv6) { - goto wrongargs; - } - - if (JimParseDomainAddress(interp, hostportarg, &sa) != JIM_OK) { - JimAioSetError(interp, argv[2]); + case SOCK_UNIX_DGRAM: + connect_addr = addr; + type = SOCK_DGRAM; + family = PF_UNIX; + /* A dgram unix domain socket client needs to bind + * to a temporary address to allow the server to + * send responses + */ + { + int tmpfd = Jim_MakeTempFile(interp, NULL, 1); + if (tmpfd < 0) { return JIM_ERR; } - family = PF_UNIX; - sock = socket(PF_UNIX, SOCK_STREAM, 0); - if (sock < 0) { - JimAioSetError(interp, NULL); - return JIM_ERR; - } - len = strlen(sa.sun_path) + 1 + sizeof(sa.sun_family); - res = bind(sock, (struct sockaddr *)&sa, len); - if (res) { - JimAioSetError(interp, argv[2]); - close(sock); - return JIM_ERR; - } - res = listen(sock, 5); - if (res) { - JimAioSetError(interp, NULL); - close(sock); - return JIM_ERR; - } - hdlfmt = "aio.sockunixsrv%ld"; - break; + close(tmpfd); + /* This will be valid until a result is next set, which is long enough here */ + bind_addr = Jim_String(Jim_GetResult(interp)); } -#endif - -#if defined(HAVE_SOCKETPAIR) && defined(HAVE_SYS_UN_H) - case SOCK_STREAM_SOCKETPAIR: - { - int p[2]; - static const char *mode[2] = { "r+", "r+" }; - - if (argc != 2 || ipv6) { - goto wrongargs; - } + break; - if (socketpair(PF_UNIX, SOCK_STREAM, 0, p) < 0) { - JimAioSetError(interp, NULL); - return JIM_ERR; - } - return JimMakeChannelPair(interp, p, argv[1], "aio.sockpair%ld", PF_UNIX, mode); + case SOCK_UNIX_SERVER: + if (addr == NULL) { + goto wrongargs; } + bind_addr = addr; + family = PF_UNIX; + do_listen = 1; break; -#endif -#if defined(HAVE_PIPE) - case SOCK_STREAM_PIPE: - if (argc != 2 || ipv6) { + case SOCK_UNIX_DGRAM_SERVER: + if (addr == NULL) { goto wrongargs; } - return JimAioPipeCommand(interp, 1, &argv[1]); + bind_addr = addr; + type = SOCK_DGRAM; + family = PF_UNIX; + break; #endif default: @@ -2038,7 +2126,46 @@ static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) return JIM_ERR; } - return JimMakeChannel(interp, NULL, sock, argv[1], hdlfmt, family, mode) ? JIM_OK : JIM_ERR; + /* Now do all the steps necessary for the given socket type */ + sock = socket(family, type, 0); + if (sock < 0) { + JimAioSetError(interp, NULL); + return JIM_ERR; + } + if (bind_addr) { + if (JimParseSocketAddress(interp, family, bind_addr, &sa, &salen) != JIM_OK) { + close(sock); + return JIM_ERR; + } + if (bind(sock, &sa.sa, salen)) { + Jim_SetResultFormatted(interp, "%s: bind: %s", bind_addr, strerror(errno)); + close(sock); + return JIM_ERR; + } + } + if (reuse) { + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); + } + if (connect_addr) { + if (JimParseSocketAddress(interp, family, connect_addr, &sa, &salen) != JIM_OK) { + close(sock); + return JIM_ERR; + } + if (connect(sock, &sa.sa, salen)) { + Jim_SetResultFormatted(interp, "%s: connect: %s", connect_addr, strerror(errno)); + close(sock); + return JIM_ERR; + } + } + if (do_listen) { + if (listen(sock, 5)) { + Jim_SetResultFormatted(interp, "listen: %s", strerror(errno)); + close(sock); + return JIM_ERR; + } + } + + return JimMakeChannel(interp, NULL, sock, argv[1], "aio.sock%ld", family, "r+", 0) ? JIM_OK : JIM_ERR; } #endif /* JIM_BOOTSTRAP */ @@ -2082,9 +2209,9 @@ int Jim_aioInit(Jim_Interp *interp) #endif /* Create filehandles for stdin, stdout and stderr */ - JimMakeChannel(interp, stdin, -1, NULL, "stdin", 0, "r"); - JimMakeChannel(interp, stdout, -1, NULL, "stdout", 0, "w"); - JimMakeChannel(interp, stderr, -1, NULL, "stderr", 0, "w"); + JimMakeChannel(interp, stdin, -1, NULL, "stdin", 0, "r", AIO_KEEPOPEN); + JimMakeChannel(interp, stdout, -1, NULL, "stdout", 0, "w", AIO_KEEPOPEN); + JimMakeChannel(interp, stderr, -1, NULL, "stderr", 0, "w", AIO_KEEPOPEN); return JIM_OK; } -- 2.11.4.GIT