aio: Don't use FD_CLOEXEC with JIM_ANSIC
[jimtcl.git] / jim-aio.c
blob589dce69afd496e6f75ea86e75c988915e7ac85a
1 /* Jim - A small embeddable Tcl interpreter
3 * Copyright 2005 Salvatore Sanfilippo <antirez@invece.org>
4 * Copyright 2005 Clemens Hintze <c.hintze@gmx.net>
5 * Copyright 2005 patthoyts - Pat Thoyts <patthoyts@users.sf.net>
6 * Copyright 2008 oharboe - Øyvind Harboe - oyvind.harboe@zylin.com
7 * Copyright 2008 Andrew Lunn <andrew@lunn.ch>
8 * Copyright 2008 Duane Ellis <openocd@duaneellis.com>
9 * Copyright 2008 Uwe Klein <uklein@klein-messgeraete.de>
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above
18 * copyright notice, this list of conditions and the following
19 * disclaimer in the documentation and/or other materials
20 * provided with the distribution.
22 * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY
23 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
24 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
25 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
33 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 * The views and conclusions contained in the software and documentation
36 * are those of the authors and should not be interpreted as representing
37 * official policies, either expressed or implied, of the Jim Tcl Project.
38 **/
40 #include "jimautoconf.h"
42 #ifndef _GNU_SOURCE
43 #define _GNU_SOURCE
44 #endif
45 #include <stdio.h>
46 #include <string.h>
47 #include <errno.h>
48 #include <fcntl.h>
49 #ifdef HAVE_UNISTD_H
50 #include <unistd.h>
51 #include <sys/stat.h>
52 #endif
54 #include "jim.h"
55 #include "jimiocompat.h"
57 #if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H)
58 #include <sys/socket.h>
59 #include <netinet/in.h>
60 #include <netinet/tcp.h>
61 #include <arpa/inet.h>
62 #include <netdb.h>
63 #ifdef HAVE_SYS_UN_H
64 #include <sys/un.h>
65 #endif
66 #define HAVE_SOCKETS
67 #elif defined (__MINGW32__)
68 /* currently mingw32 doesn't support sockets, but has pipe, fdopen */
69 #else
70 #define JIM_ANSIC
71 #endif
73 #if defined(JIM_SSL)
74 #include <openssl/ssl.h>
75 #include <openssl/err.h>
76 #endif
78 #ifdef HAVE_TERMIOS_H
79 #include <jim-tty.h>
80 #endif
82 #include "jim-eventloop.h"
83 #include "jim-subcmd.h"
85 #define AIO_CMD_LEN 32 /* e.g. aio.handleXXXXXX */
86 #define AIO_BUF_LEN 256 /* Can keep this small and rely on stdio buffering */
88 #ifndef HAVE_FTELLO
89 #define ftello ftell
90 #endif
91 #ifndef HAVE_FSEEKO
92 #define fseeko fseek
93 #endif
95 #define AIO_KEEPOPEN 1
97 #if defined(JIM_IPV6)
98 #define IPV6 1
99 #else
100 #define IPV6 0
101 #ifndef PF_INET6
102 #define PF_INET6 0
103 #endif
104 #endif
106 #ifdef JIM_ANSIC
107 /* no fdopen() with ANSIC, so can't support these */
108 #undef HAVE_PIPE
109 #undef HAVE_SOCKETPAIR
110 #endif
112 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
113 union sockaddr_any {
114 struct sockaddr sa;
115 struct sockaddr_in sin;
116 #if IPV6
117 struct sockaddr_in6 sin6;
118 #endif
121 #ifndef HAVE_INET_NTOP
122 const char *inet_ntop(int af, const void *src, char *dst, int size)
124 if (af != PF_INET) {
125 return NULL;
127 snprintf(dst, size, "%s", inet_ntoa(((struct sockaddr_in *)src)->sin_addr));
128 return dst;
130 #endif
131 #endif /* JIM_BOOTSTRAP */
133 struct AioFile;
135 typedef struct {
136 int (*writer)(struct AioFile *af, const char *buf, int len);
137 int (*reader)(struct AioFile *af, char *buf, int len);
138 const char *(*getline)(struct AioFile *af, char *buf, int len);
139 int (*error)(const struct AioFile *af);
140 const char *(*strerror)(struct AioFile *af);
141 int (*verify)(struct AioFile *af);
142 } JimAioFopsType;
144 typedef struct AioFile
146 FILE *fp;
147 Jim_Obj *filename;
148 int type;
149 int openFlags; /* AIO_KEEPOPEN? keep FILE* */
150 int fd;
151 Jim_Obj *rEvent;
152 Jim_Obj *wEvent;
153 Jim_Obj *eEvent;
154 int addr_family;
155 void *ssl;
156 const JimAioFopsType *fops;
157 } AioFile;
159 static int stdio_writer(struct AioFile *af, const char *buf, int len)
161 return fwrite(buf, 1, len, af->fp);
164 static int stdio_reader(struct AioFile *af, char *buf, int len)
166 return fread(buf, 1, len, af->fp);
169 static const char *stdio_getline(struct AioFile *af, char *buf, int len)
171 return fgets(buf, len, af->fp);
174 static int stdio_error(const AioFile *af)
176 if (!ferror(af->fp)) {
177 return JIM_OK;
179 clearerr(af->fp);
180 /* EAGAIN and similar are not error conditions. Just treat them like eof */
181 if (feof(af->fp) || errno == EAGAIN || errno == EINTR) {
182 return JIM_OK;
184 #ifdef ECONNRESET
185 if (errno == ECONNRESET) {
186 return JIM_OK;
188 #endif
189 #ifdef ECONNABORTED
190 if (errno == ECONNABORTED) {
191 return JIM_OK;
193 #endif
194 return JIM_ERR;
197 static const char *stdio_strerror(struct AioFile *af)
199 return strerror(errno);
202 static const JimAioFopsType stdio_fops = {
203 stdio_writer,
204 stdio_reader,
205 stdio_getline,
206 stdio_error,
207 stdio_strerror,
208 NULL
211 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
213 static SSL_CTX *JimAioSslCtx(Jim_Interp *interp);
215 static int ssl_writer(struct AioFile *af, const char *buf, int len)
217 return SSL_write(af->ssl, buf, len);
220 static int ssl_reader(struct AioFile *af, char *buf, int len)
222 return SSL_read(af->ssl, buf, len);
225 static const char *ssl_getline(struct AioFile *af, char *buf, int len)
227 size_t i;
228 for (i = 0; i < len + 1; i++) {
229 if (SSL_read(af->ssl, &buf[i], 1) != 1) {
230 if (i == 0) {
231 return NULL;
233 break;
235 if (buf[i] == '\n') {
236 break;
239 buf[i] = '\0';
240 return buf;
243 static int ssl_error(const struct AioFile *af)
245 if (ERR_peek_error() == 0) {
246 return JIM_OK;
249 return JIM_ERR;
252 static const char *ssl_strerror(struct AioFile *af)
254 int err = ERR_get_error();
256 if (err) {
257 return ERR_error_string(err, NULL);
259 else {
260 return stdio_strerror(af);
264 static int ssl_verify(struct AioFile *af)
266 X509 *cert;
268 cert = SSL_get_peer_certificate(af->ssl);
269 if (!cert) {
270 return JIM_ERR;
272 X509_free(cert);
274 if (SSL_get_verify_result(af->ssl) == X509_V_OK) {
275 return JIM_OK;
278 return JIM_ERR;
281 static const JimAioFopsType ssl_fops = {
282 ssl_writer,
283 ssl_reader,
284 ssl_getline,
285 ssl_error,
286 ssl_strerror,
287 ssl_verify
289 #endif /* JIM_BOOTSTRAP */
291 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
292 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
293 const char *hdlfmt, int family, const char *mode);
295 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
296 static int JimParseIPv6Address(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, int *salen)
298 #if IPV6
300 * An IPv6 addr/port looks like:
301 * [::1]
302 * [::1]:2000
303 * [fe80::223:6cff:fe95:bdc0%en1]:2000
304 * [::]:2000
305 * 2000
307 * Note that the "any" address is ::, which is the same as when no address is specified.
309 char *sthost = NULL;
310 const char *stport;
311 int ret = JIM_OK;
312 struct addrinfo req;
313 struct addrinfo *ai;
315 stport = strrchr(hostport, ':');
316 if (!stport) {
317 /* No : so, the whole thing is the port */
318 stport = hostport;
319 hostport = "::";
320 sthost = Jim_StrDup(hostport);
322 else {
323 stport++;
326 if (*hostport == '[') {
327 /* This is a numeric ipv6 address */
328 char *pt = strchr(++hostport, ']');
329 if (pt) {
330 sthost = Jim_StrDupLen(hostport, pt - hostport);
334 if (!sthost) {
335 sthost = Jim_StrDupLen(hostport, stport - hostport - 1);
338 memset(&req, '\0', sizeof(req));
339 req.ai_family = PF_INET6;
341 if (getaddrinfo(sthost, NULL, &req, &ai)) {
342 Jim_SetResultFormatted(interp, "Not a valid address: %s", hostport);
343 ret = JIM_ERR;
345 else {
346 memcpy(&sa->sin, ai->ai_addr, ai->ai_addrlen);
347 *salen = ai->ai_addrlen;
349 sa->sin.sin_port = htons(atoi(stport));
351 freeaddrinfo(ai);
353 Jim_Free(sthost);
355 return ret;
356 #else
357 Jim_SetResultString(interp, "ipv6 not supported", -1);
358 return JIM_ERR;
359 #endif
362 static int JimParseIpAddress(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, int *salen)
364 /* An IPv4 addr/port looks like:
365 * 192.168.1.5
366 * 192.168.1.5:2000
367 * 2000
369 * If the address is missing, INADDR_ANY is used.
370 * If the port is missing, 0 is used (only useful for server sockets).
372 char *sthost = NULL;
373 const char *stport;
374 int ret = JIM_OK;
376 stport = strrchr(hostport, ':');
377 if (!stport) {
378 /* No : so, the whole thing is the port */
379 stport = hostport;
380 sthost = Jim_StrDup("0.0.0.0");
382 else {
383 sthost = Jim_StrDupLen(hostport, stport - hostport);
384 stport++;
388 #ifdef HAVE_GETADDRINFO
389 struct addrinfo req;
390 struct addrinfo *ai;
391 memset(&req, '\0', sizeof(req));
392 req.ai_family = PF_INET;
394 if (getaddrinfo(sthost, NULL, &req, &ai)) {
395 ret = JIM_ERR;
397 else {
398 memcpy(&sa->sin, ai->ai_addr, ai->ai_addrlen);
399 *salen = ai->ai_addrlen;
400 freeaddrinfo(ai);
402 #else
403 struct hostent *he;
405 ret = JIM_ERR;
407 if ((he = gethostbyname(sthost)) != NULL) {
408 if (he->h_length == sizeof(sa->sin.sin_addr)) {
409 *salen = sizeof(sa->sin);
410 sa->sin.sin_family= he->h_addrtype;
411 memcpy(&sa->sin.sin_addr, he->h_addr, he->h_length); /* set address */
412 ret = JIM_OK;
415 #endif
417 sa->sin.sin_port = htons(atoi(stport));
419 Jim_Free(sthost);
421 if (ret != JIM_OK) {
422 Jim_SetResultFormatted(interp, "Not a valid address: %s", hostport);
425 return ret;
428 #ifdef HAVE_SYS_UN_H
429 static int JimParseDomainAddress(Jim_Interp *interp, const char *path, struct sockaddr_un *sa)
431 sa->sun_family = PF_UNIX;
432 snprintf(sa->sun_path, sizeof(sa->sun_path), "%s", path);
434 return JIM_OK;
436 #endif
439 * Format that address in 'sa' as a string and store in variable 'varObjPtr'
441 static int JimFormatIpAddress(Jim_Interp *interp, Jim_Obj *varObjPtr, const union sockaddr_any *sa)
443 /* INET6_ADDRSTRLEN is 46. Add some for [] and port */
444 char addrbuf[60];
446 #if IPV6
447 if (sa->sa.sa_family == PF_INET6) {
448 addrbuf[0] = '[';
449 /* Allow 9 for []:65535\0 */
450 inet_ntop(sa->sa.sa_family, &sa->sin6.sin6_addr, addrbuf + 1, sizeof(addrbuf) - 9);
451 snprintf(addrbuf + strlen(addrbuf), 8, "]:%d", ntohs(sa->sin.sin_port));
453 else
454 #endif
455 if (sa->sa.sa_family == PF_INET) {
456 /* Allow 7 for :65535\0 */
457 inet_ntop(sa->sa.sa_family, &sa->sin.sin_addr, addrbuf, sizeof(addrbuf) - 7);
458 snprintf(addrbuf + strlen(addrbuf), 7, ":%d", ntohs(sa->sin.sin_port));
460 else {
461 /* recvfrom still works on unix domain sockets, etc */
462 addrbuf[0] = 0;
465 return Jim_SetVariable(interp, varObjPtr, Jim_NewStringObj(interp, addrbuf, -1));
468 #endif /* JIM_BOOTSTRAP */
470 static const char *JimAioErrorString(AioFile *af)
472 if (af && af->fops)
473 return af->fops->strerror(af);
475 return strerror(errno);
478 static void JimAioSetError(Jim_Interp *interp, Jim_Obj *name)
480 AioFile *af = Jim_CmdPrivData(interp);
482 if (name) {
483 Jim_SetResultFormatted(interp, "%#s: %s", name, JimAioErrorString(af));
485 else {
486 Jim_SetResultString(interp, JimAioErrorString(af), -1);
490 static int JimCheckStreamError(Jim_Interp *interp, AioFile *af)
492 int ret = af->fops->error(af);
493 if (ret) {
494 JimAioSetError(interp, af->filename);
496 return ret;
499 static void JimAioDelProc(Jim_Interp *interp, void *privData)
501 AioFile *af = privData;
503 JIM_NOTUSED(interp);
505 Jim_DecrRefCount(interp, af->filename);
507 #ifdef jim_ext_eventloop
508 /* remove all existing EventHandlers */
509 Jim_DeleteFileHandler(interp, af->fd, JIM_EVENT_READABLE | JIM_EVENT_WRITABLE | JIM_EVENT_EXCEPTION);
510 #endif
512 #if defined(JIM_SSL)
513 if (af->ssl != NULL) {
514 SSL_free(af->ssl);
516 #endif
517 if (!(af->openFlags & AIO_KEEPOPEN)) {
518 fclose(af->fp);
521 Jim_Free(af);
524 static int aio_cmd_read(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
526 AioFile *af = Jim_CmdPrivData(interp);
527 char buf[AIO_BUF_LEN];
528 Jim_Obj *objPtr;
529 int nonewline = 0;
530 jim_wide neededLen = -1; /* -1 is "read as much as possible" */
532 if (argc && Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
533 nonewline = 1;
534 argv++;
535 argc--;
537 if (argc == 1) {
538 if (Jim_GetWide(interp, argv[0], &neededLen) != JIM_OK)
539 return JIM_ERR;
540 if (neededLen < 0) {
541 Jim_SetResultString(interp, "invalid parameter: negative len", -1);
542 return JIM_ERR;
545 else if (argc) {
546 return -1;
548 objPtr = Jim_NewStringObj(interp, NULL, 0);
549 while (neededLen != 0) {
550 int retval;
551 int readlen;
553 if (neededLen == -1) {
554 readlen = AIO_BUF_LEN;
556 else {
557 readlen = (neededLen > AIO_BUF_LEN ? AIO_BUF_LEN : neededLen);
559 retval = af->fops->reader(af, buf, readlen);
560 if (retval > 0) {
561 Jim_AppendString(interp, objPtr, buf, retval);
562 if (neededLen != -1) {
563 neededLen -= retval;
566 if (retval != readlen)
567 break;
569 /* Check for error conditions */
570 if (JimCheckStreamError(interp, af)) {
571 Jim_FreeNewObj(interp, objPtr);
572 return JIM_ERR;
574 if (nonewline) {
575 int len;
576 const char *s = Jim_GetString(objPtr, &len);
578 if (len > 0 && s[len - 1] == '\n') {
579 objPtr->length--;
580 objPtr->bytes[objPtr->length] = '\0';
583 Jim_SetResult(interp, objPtr);
584 return JIM_OK;
587 AioFile *Jim_AioFile(Jim_Interp *interp, Jim_Obj *command)
589 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, command, JIM_ERRMSG);
591 /* XXX: There ought to be a supported API for this */
592 if (cmdPtr && !cmdPtr->isproc && cmdPtr->u.native.cmdProc == JimAioSubCmdProc) {
593 return (AioFile *) cmdPtr->u.native.privData;
595 Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", command);
596 return NULL;
599 FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command)
601 AioFile *af;
603 af = Jim_AioFile(interp, command);
604 if (af == NULL) {
605 return NULL;
608 return af->fp;
611 static int aio_cmd_getfd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
613 AioFile *af = Jim_CmdPrivData(interp);
615 fflush(af->fp);
616 Jim_SetResultInt(interp, fileno(af->fp));
618 return JIM_OK;
621 static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
623 AioFile *af = Jim_CmdPrivData(interp);
624 jim_wide count = 0;
625 jim_wide maxlen = JIM_WIDE_MAX;
626 AioFile *outf = Jim_AioFile(interp, argv[0]);
628 if (outf == NULL) {
629 return JIM_ERR;
632 if (argc == 2) {
633 if (Jim_GetWide(interp, argv[1], &maxlen) != JIM_OK) {
634 return JIM_ERR;
638 while (count < maxlen) {
639 char ch;
641 if (af->fops->reader(af, &ch, 1) != 1) {
642 break;
644 if (outf->fops->writer(outf, &ch, 1) != 1) {
645 break;
647 count++;
650 if (JimCheckStreamError(interp, af) || JimCheckStreamError(interp, outf)) {
651 return JIM_ERR;
654 Jim_SetResultInt(interp, count);
656 return JIM_OK;
659 static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
661 AioFile *af = Jim_CmdPrivData(interp);
662 char buf[AIO_BUF_LEN];
663 Jim_Obj *objPtr;
664 int len;
666 errno = 0;
668 objPtr = Jim_NewStringObj(interp, NULL, 0);
669 while (1) {
670 buf[AIO_BUF_LEN - 1] = '_';
672 if (af->fops->getline(af, buf, AIO_BUF_LEN) == NULL)
673 break;
675 if (buf[AIO_BUF_LEN - 1] == '\0' && buf[AIO_BUF_LEN - 2] != '\n') {
676 Jim_AppendString(interp, objPtr, buf, AIO_BUF_LEN - 1);
678 else {
679 len = strlen(buf);
681 if (len && (buf[len - 1] == '\n')) {
682 /* strip "\n" */
683 len--;
686 Jim_AppendString(interp, objPtr, buf, len);
687 break;
691 if (JimCheckStreamError(interp, af)) {
692 /* I/O error */
693 Jim_FreeNewObj(interp, objPtr);
694 return JIM_ERR;
697 if (argc) {
698 if (Jim_SetVariable(interp, argv[0], objPtr) != JIM_OK) {
699 Jim_FreeNewObj(interp, objPtr);
700 return JIM_ERR;
703 len = Jim_Length(objPtr);
705 if (len == 0 && feof(af->fp)) {
706 /* On EOF returns -1 if varName was specified */
707 len = -1;
709 Jim_SetResultInt(interp, len);
711 else {
712 Jim_SetResult(interp, objPtr);
714 return JIM_OK;
717 static int aio_cmd_puts(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
719 AioFile *af = Jim_CmdPrivData(interp);
720 int wlen;
721 const char *wdata;
722 Jim_Obj *strObj;
724 if (argc == 2) {
725 if (!Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
726 return -1;
728 strObj = argv[1];
730 else {
731 strObj = argv[0];
734 wdata = Jim_GetString(strObj, &wlen);
735 if (af->fops->writer(af, wdata, wlen) == wlen) {
736 if (argc == 2 || af->fops->writer(af, "\n", 1) == 1) {
737 return JIM_OK;
740 JimAioSetError(interp, af->filename);
741 return JIM_ERR;
744 static int aio_cmd_isatty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
746 #ifdef HAVE_ISATTY
747 AioFile *af = Jim_CmdPrivData(interp);
748 Jim_SetResultInt(interp, isatty(fileno(af->fp)));
749 #else
750 Jim_SetResultInt(interp, 0);
751 #endif
753 return JIM_OK;
756 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
757 static int aio_cmd_recvfrom(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
759 AioFile *af = Jim_CmdPrivData(interp);
760 char *buf;
761 union sockaddr_any sa;
762 long len;
763 socklen_t salen = sizeof(sa);
764 int rlen;
766 if (Jim_GetLong(interp, argv[0], &len) != JIM_OK) {
767 return JIM_ERR;
770 buf = Jim_Alloc(len + 1);
772 rlen = recvfrom(fileno(af->fp), buf, len, 0, &sa.sa, &salen);
773 if (rlen < 0) {
774 Jim_Free(buf);
775 JimAioSetError(interp, NULL);
776 return JIM_ERR;
778 buf[rlen] = 0;
779 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, rlen));
781 if (argc > 1) {
782 return JimFormatIpAddress(interp, argv[1], &sa);
785 return JIM_OK;
789 static int aio_cmd_sendto(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
791 AioFile *af = Jim_CmdPrivData(interp);
792 int wlen;
793 int len;
794 const char *wdata;
795 union sockaddr_any sa;
796 const char *addr = Jim_String(argv[1]);
797 int salen;
799 if (IPV6 && af->addr_family == PF_INET6) {
800 if (JimParseIPv6Address(interp, addr, &sa, &salen) != JIM_OK) {
801 return JIM_ERR;
804 else if (JimParseIpAddress(interp, addr, &sa, &salen) != JIM_OK) {
805 return JIM_ERR;
807 wdata = Jim_GetString(argv[0], &wlen);
809 /* Note that we don't validate the socket type. Rely on sendto() failing if appropriate */
810 len = sendto(fileno(af->fp), wdata, wlen, 0, &sa.sa, salen);
811 if (len < 0) {
812 JimAioSetError(interp, NULL);
813 return JIM_ERR;
815 Jim_SetResultInt(interp, len);
816 return JIM_OK;
819 static int aio_cmd_accept(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
821 AioFile *af = Jim_CmdPrivData(interp);
822 int sock;
823 union sockaddr_any sa;
824 socklen_t addrlen = sizeof(sa);
826 sock = accept(af->fd, &sa.sa, &addrlen);
827 if (sock < 0) {
828 JimAioSetError(interp, NULL);
829 return JIM_ERR;
832 if (argc > 0) {
833 if (JimFormatIpAddress(interp, argv[0], &sa) != JIM_OK) {
834 return JIM_ERR;
838 /* Create the file command */
839 return JimMakeChannel(interp, NULL, sock, Jim_NewStringObj(interp, "accept", -1),
840 "aio.sockstream%ld", af->addr_family, "r+") ? JIM_OK : JIM_ERR;
843 static int aio_cmd_listen(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
845 AioFile *af = Jim_CmdPrivData(interp);
846 long backlog;
848 if (Jim_GetLong(interp, argv[0], &backlog) != JIM_OK) {
849 return JIM_ERR;
852 if (listen(af->fd, backlog)) {
853 JimAioSetError(interp, NULL);
854 return JIM_ERR;
857 return JIM_OK;
859 #endif /* JIM_BOOTSTRAP */
861 static int aio_cmd_flush(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
863 AioFile *af = Jim_CmdPrivData(interp);
865 if (fflush(af->fp) == EOF) {
866 JimAioSetError(interp, af->filename);
867 return JIM_ERR;
869 return JIM_OK;
872 static int aio_cmd_eof(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
874 AioFile *af = Jim_CmdPrivData(interp);
876 Jim_SetResultInt(interp, feof(af->fp));
877 return JIM_OK;
880 static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
882 if (argc == 3) {
883 #if defined(HAVE_SOCKETS) && defined(HAVE_SHUTDOWN)
884 static const char * const options[] = { "r", "w", NULL };
885 enum { OPT_R, OPT_W, };
886 int option;
887 AioFile *af = Jim_CmdPrivData(interp);
889 if (Jim_GetEnum(interp, argv[2], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
890 return JIM_ERR;
892 if (shutdown(af->fd, option == OPT_R ? SHUT_RD : SHUT_WR) == 0) {
893 return JIM_OK;
895 JimAioSetError(interp, NULL);
896 #else
897 Jim_SetResultString(interp, "async close not supported", -1);
898 #endif
899 return JIM_ERR;
902 return Jim_DeleteCommand(interp, Jim_String(argv[0]));
905 static int aio_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
907 AioFile *af = Jim_CmdPrivData(interp);
908 int orig = SEEK_SET;
909 jim_wide offset;
911 if (argc == 2) {
912 if (Jim_CompareStringImmediate(interp, argv[1], "start"))
913 orig = SEEK_SET;
914 else if (Jim_CompareStringImmediate(interp, argv[1], "current"))
915 orig = SEEK_CUR;
916 else if (Jim_CompareStringImmediate(interp, argv[1], "end"))
917 orig = SEEK_END;
918 else {
919 return -1;
922 if (Jim_GetWide(interp, argv[0], &offset) != JIM_OK) {
923 return JIM_ERR;
925 if (fseeko(af->fp, offset, orig) == -1) {
926 JimAioSetError(interp, af->filename);
927 return JIM_ERR;
929 return JIM_OK;
932 static int aio_cmd_tell(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
934 AioFile *af = Jim_CmdPrivData(interp);
936 Jim_SetResultInt(interp, ftello(af->fp));
937 return JIM_OK;
940 static int aio_cmd_filename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
942 AioFile *af = Jim_CmdPrivData(interp);
944 Jim_SetResult(interp, af->filename);
945 return JIM_OK;
948 #ifdef O_NDELAY
949 static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
951 AioFile *af = Jim_CmdPrivData(interp);
953 int fmode = fcntl(af->fd, F_GETFL);
955 if (argc) {
956 long nb;
958 if (Jim_GetLong(interp, argv[0], &nb) != JIM_OK) {
959 return JIM_ERR;
961 if (nb) {
962 fmode |= O_NDELAY;
964 else {
965 fmode &= ~O_NDELAY;
967 (void)fcntl(af->fd, F_SETFL, fmode);
969 Jim_SetResultInt(interp, (fmode & O_NONBLOCK) ? 1 : 0);
970 return JIM_OK;
972 #endif
974 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
975 #define SOCKOPT_BOOL 0
976 #define SOCKOPT_INT 1
977 #define SOCKOPT_TIMEVAL 2 /* not currently supported */
979 static const struct sockopt_def {
980 const char *name;
981 int level;
982 int opt;
983 int type; /* SOCKOPT_xxx */
984 } sockopts[] = {
985 #ifdef SOL_SOCKET
986 #ifdef SO_BROADCAST
987 { "broadcast", SOL_SOCKET, SO_BROADCAST },
988 #endif
989 #ifdef SO_DEBUG
990 { "debug", SOL_SOCKET, SO_DEBUG },
991 #endif
992 #ifdef SO_KEEPALIVE
993 { "keepalive", SOL_SOCKET, SO_KEEPALIVE },
994 #endif
995 #ifdef SO_NOSIGPIPE
996 { "nosigpipe", SOL_SOCKET, SO_NOSIGPIPE },
997 #endif
998 #ifdef SO_OOBINLINE
999 { "oobinline", SOL_SOCKET, SO_OOBINLINE },
1000 #endif
1001 #ifdef SO_SNDBUF
1002 { "sndbuf", SOL_SOCKET, SO_SNDBUF, SOCKOPT_INT },
1003 #endif
1004 #ifdef SO_RCVBUF
1005 { "rcvbuf", SOL_SOCKET, SO_RCVBUF, SOCKOPT_INT },
1006 #endif
1007 #if 0 && defined(SO_SNDTIMEO)
1008 { "sndtimeo", SOL_SOCKET, SO_SNDTIMEO, SOCKOPT_TIMEVAL },
1009 #endif
1010 #if 0 && defined(SO_RCVTIMEO)
1011 { "rcvtimeo", SOL_SOCKET, SO_RCVTIMEO, SOCKOPT_TIMEVAL },
1012 #endif
1013 #endif
1014 #ifdef IPPROTO_TCP
1015 #ifdef TCP_NODELAY
1016 { "tcp_nodelay", IPPROTO_TCP, TCP_NODELAY },
1017 #endif
1018 #endif
1021 static int aio_cmd_sockopt(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1023 AioFile *af = Jim_CmdPrivData(interp);
1024 int i;
1026 if (argc == 0) {
1027 Jim_Obj *dictObjPtr = Jim_NewListObj(interp, NULL, 0);
1028 for (i = 0; i < sizeof(sockopts) / sizeof(*sockopts); i++) {
1029 int value = 0;
1030 socklen_t len = sizeof(value);
1031 if (getsockopt(af->fd, sockopts[i].level, sockopts[i].opt, (void *)&value, &len) == 0) {
1032 if (sockopts[i].type == SOCKOPT_BOOL) {
1033 value = !!value;
1035 Jim_ListAppendElement(interp, dictObjPtr, Jim_NewStringObj(interp, sockopts[i].name, -1));
1036 Jim_ListAppendElement(interp, dictObjPtr, Jim_NewIntObj(interp, value));
1039 Jim_SetResult(interp, dictObjPtr);
1040 return JIM_OK;
1042 if (argc == 1) {
1043 return -1;
1046 /* Set an option */
1047 for (i = 0; i < sizeof(sockopts) / sizeof(*sockopts); i++) {
1048 if (strcmp(Jim_String(argv[0]), sockopts[i].name) == 0) {
1049 int on;
1050 if (sockopts[i].type == SOCKOPT_BOOL) {
1051 if (Jim_GetBoolean(interp, argv[1], &on) != JIM_OK) {
1052 return JIM_ERR;
1055 else {
1056 long longval;
1057 if (Jim_GetLong(interp, argv[1], &longval) != JIM_OK) {
1058 return JIM_ERR;
1060 on = longval;
1062 if (setsockopt(af->fd, sockopts[i].level, sockopts[i].opt, (void *)&on, sizeof(on)) < 0) {
1063 Jim_SetResultFormatted(interp, "Failed to set %#s: %s", argv[0], strerror(errno));
1064 return JIM_ERR;
1066 return JIM_OK;
1069 /* Not found */
1070 Jim_SetResultFormatted(interp, "Unknown sockopt %#s", argv[0]);
1071 return JIM_ERR;
1073 #endif /* JIM_BOOTSTRAP */
1075 #ifdef HAVE_FSYNC
1076 static int aio_cmd_sync(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1078 AioFile *af = Jim_CmdPrivData(interp);
1080 fflush(af->fp);
1081 fsync(af->fd);
1082 return JIM_OK;
1084 #endif
1086 static int aio_cmd_buffering(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1088 AioFile *af = Jim_CmdPrivData(interp);
1090 static const char * const options[] = {
1091 "none",
1092 "line",
1093 "full",
1094 NULL
1096 enum
1098 OPT_NONE,
1099 OPT_LINE,
1100 OPT_FULL,
1102 int option;
1104 if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
1105 return JIM_ERR;
1107 switch (option) {
1108 case OPT_NONE:
1109 setvbuf(af->fp, NULL, _IONBF, 0);
1110 break;
1111 case OPT_LINE:
1112 setvbuf(af->fp, NULL, _IOLBF, BUFSIZ);
1113 break;
1114 case OPT_FULL:
1115 setvbuf(af->fp, NULL, _IOFBF, BUFSIZ);
1116 break;
1118 return JIM_OK;
1121 #ifdef jim_ext_eventloop
1122 static void JimAioFileEventFinalizer(Jim_Interp *interp, void *clientData)
1124 Jim_Obj **objPtrPtr = clientData;
1126 Jim_DecrRefCount(interp, *objPtrPtr);
1127 *objPtrPtr = NULL;
1130 static int JimAioFileEventHandler(Jim_Interp *interp, void *clientData, int mask)
1132 Jim_Obj **objPtrPtr = clientData;
1134 return Jim_EvalObjBackground(interp, *objPtrPtr);
1137 static int aio_eventinfo(Jim_Interp *interp, AioFile * af, unsigned mask, Jim_Obj **scriptHandlerObj,
1138 int argc, Jim_Obj * const *argv)
1140 if (argc == 0) {
1141 /* Return current script */
1142 if (*scriptHandlerObj) {
1143 Jim_SetResult(interp, *scriptHandlerObj);
1145 return JIM_OK;
1148 if (*scriptHandlerObj) {
1149 /* Delete old handler */
1150 Jim_DeleteFileHandler(interp, af->fd, mask);
1153 /* Now possibly add the new script(s) */
1154 if (Jim_Length(argv[0]) == 0) {
1155 /* Empty script, so done */
1156 return JIM_OK;
1159 /* A new script to add */
1160 Jim_IncrRefCount(argv[0]);
1161 *scriptHandlerObj = argv[0];
1163 Jim_CreateFileHandler(interp, af->fd, mask,
1164 JimAioFileEventHandler, scriptHandlerObj, JimAioFileEventFinalizer);
1166 return JIM_OK;
1169 static int aio_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1171 AioFile *af = Jim_CmdPrivData(interp);
1173 return aio_eventinfo(interp, af, JIM_EVENT_READABLE, &af->rEvent, argc, argv);
1176 static int aio_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1178 AioFile *af = Jim_CmdPrivData(interp);
1180 return aio_eventinfo(interp, af, JIM_EVENT_WRITABLE, &af->wEvent, argc, argv);
1183 static int aio_cmd_onexception(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1185 AioFile *af = Jim_CmdPrivData(interp);
1187 return aio_eventinfo(interp, af, JIM_EVENT_EXCEPTION, &af->eEvent, argc, argv);
1189 #endif
1191 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1192 static int aio_cmd_ssl(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1194 AioFile *af = Jim_CmdPrivData(interp);
1195 SSL *ssl;
1196 SSL_CTX *ssl_ctx;
1197 int server = 0;
1199 if (argc == 5) {
1200 if (!Jim_CompareStringImmediate(interp, argv[2], "-server")) {
1201 return JIM_ERR;
1203 server = 1;
1205 else if (argc != 2) {
1206 Jim_WrongNumArgs(interp, 2, argv, "?-server cert priv?");
1207 return JIM_ERR;
1210 if (af->ssl) {
1211 Jim_SetResultFormatted(interp, "%#s: stream is already ssl", argv[0]);
1212 return JIM_ERR;
1215 ssl_ctx = JimAioSslCtx(interp);
1216 if (ssl_ctx == NULL) {
1217 return JIM_ERR;
1220 ssl = SSL_new(ssl_ctx);
1221 if (ssl == NULL) {
1222 goto out;
1225 SSL_set_cipher_list(ssl, "ALL");
1227 if (SSL_set_fd(ssl, fileno(af->fp)) == 0) {
1228 goto out;
1231 if (server) {
1232 if (SSL_use_certificate_file(ssl, Jim_String(argv[3]), SSL_FILETYPE_PEM) != 1) {
1233 goto out;
1236 if (SSL_use_PrivateKey_file(ssl, Jim_String(argv[4]), SSL_FILETYPE_PEM) != 1) {
1237 goto out;
1240 if (SSL_accept(ssl) != 1) {
1241 goto out;
1244 else {
1245 if (SSL_connect(ssl) != 1) {
1246 goto out;
1250 af->ssl = ssl;
1251 af->fops = &ssl_fops;
1253 /* Set the command name as the result */
1254 Jim_SetResult(interp, argv[0]);
1256 return JIM_OK;
1258 out:
1259 if (ssl) {
1260 SSL_free(ssl);
1262 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1263 return JIM_ERR;
1266 static int aio_cmd_verify(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1268 AioFile *af = Jim_CmdPrivData(interp);
1269 int ret;
1271 if (!af->fops->verify) {
1272 return JIM_OK;
1275 ret = af->fops->verify(af);
1276 if (ret != JIM_OK) {
1277 if (JimCheckStreamError(interp, af) == JIM_OK) {
1278 Jim_SetResultString(interp, "failed to verify the connection authenticity", -1);
1281 return ret;
1283 #endif /* JIM_BOOTSTRAP */
1285 #if defined(HAVE_STRUCT_FLOCK) && !defined(JIM_BOOTSTRAP)
1286 static int aio_cmd_lock(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1288 AioFile *af = Jim_CmdPrivData(interp);
1289 struct flock fl;
1291 fl.l_start = 0;
1292 fl.l_len = 0;
1293 fl.l_type = F_WRLCK;
1294 fl.l_whence = SEEK_SET;
1296 switch (fcntl(af->fd, F_SETLK, &fl))
1298 case 0:
1299 Jim_SetResultInt(interp, 1);
1300 break;
1301 case -1:
1302 if (errno == EACCES || errno == EAGAIN)
1303 Jim_SetResultInt(interp, 0);
1304 else
1306 Jim_SetResultFormatted(interp, "lock failed: %s",
1307 strerror(errno));
1308 return JIM_ERR;
1310 break;
1311 default:
1312 Jim_SetResultInt(interp, 0);
1313 break;
1316 return JIM_OK;
1319 static int aio_cmd_unlock(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1321 AioFile *af = Jim_CmdPrivData(interp);
1322 struct flock fl;
1323 fl.l_start = 0;
1324 fl.l_len = 0;
1325 fl.l_type = F_UNLCK;
1326 fl.l_whence = SEEK_SET;
1328 Jim_SetResultInt(interp, fcntl(af->fd, F_SETLK, &fl) == 0);
1329 return JIM_OK;
1331 #endif /* JIM_BOOTSTRAP */
1333 #if defined(HAVE_TERMIOS_H) && !defined(JIM_BOOTSTRAP)
1334 static int aio_cmd_tty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1336 AioFile *af = Jim_CmdPrivData(interp);
1337 Jim_Obj *dictObjPtr;
1338 int ret;
1340 if (argc == 0) {
1341 /* get the current settings as a dictionary */
1342 dictObjPtr = Jim_GetTtySettings(interp, af->fd);
1343 if (dictObjPtr == NULL) {
1344 JimAioSetError(interp, NULL);
1345 return JIM_ERR;
1347 Jim_SetResult(interp, dictObjPtr);
1348 return JIM_OK;
1351 if (argc > 1) {
1352 /* Convert name value arguments to a dictionary */
1353 dictObjPtr = Jim_NewListObj(interp, argv, argc);
1355 else {
1356 /* The settings are already given as a list */
1357 dictObjPtr = argv[0];
1359 Jim_IncrRefCount(dictObjPtr);
1361 if (Jim_ListLength(interp, dictObjPtr) % 2) {
1362 /* Must be a valid dictionary */
1363 Jim_DecrRefCount(interp, dictObjPtr);
1364 return -1;
1367 ret = Jim_SetTtySettings(interp, af->fd, dictObjPtr);
1368 if (ret < 0) {
1369 JimAioSetError(interp, NULL);
1370 ret = JIM_ERR;
1372 Jim_DecrRefCount(interp, dictObjPtr);
1374 return ret;
1376 #endif /* JIM_BOOTSTRAP */
1378 static const jim_subcmd_type aio_command_table[] = {
1379 { "read",
1380 "?-nonewline? ?len?",
1381 aio_cmd_read,
1384 /* Description: Read and return bytes from the stream. To eof if no len. */
1386 { "copyto",
1387 "handle ?size?",
1388 aio_cmd_copy,
1391 /* Description: Copy up to 'size' bytes to the given filehandle, or to eof if no size. */
1393 { "getfd",
1394 NULL,
1395 aio_cmd_getfd,
1398 /* Description: Internal command to return the underlying file descriptor. */
1400 { "gets",
1401 "?var?",
1402 aio_cmd_gets,
1405 /* Description: Read one line and return it or store it in the var */
1407 { "puts",
1408 "?-nonewline? str",
1409 aio_cmd_puts,
1412 /* Description: Write the string, with newline unless -nonewline */
1414 { "isatty",
1415 NULL,
1416 aio_cmd_isatty,
1419 /* Description: Is the file descriptor a tty? */
1421 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
1422 { "recvfrom",
1423 "len ?addrvar?",
1424 aio_cmd_recvfrom,
1427 /* Description: Receive up to 'len' bytes on the socket. Sets 'addrvar' with receive address, if set */
1429 { "sendto",
1430 "str address",
1431 aio_cmd_sendto,
1434 /* Description: Send 'str' to the given address (dgram only) */
1436 { "accept",
1437 "?addrvar?",
1438 aio_cmd_accept,
1441 /* Description: Server socket only: Accept a connection and return stream */
1443 { "listen",
1444 "backlog",
1445 aio_cmd_listen,
1448 /* Description: Set the listen backlog for server socket */
1450 { "sockopt",
1451 "?opt 0|1?",
1452 aio_cmd_sockopt,
1455 /* Description: Return a dictionary of sockopts, or set the value of a sockopt */
1457 #endif /* JIM_BOOTSTRAP */
1458 { "flush",
1459 NULL,
1460 aio_cmd_flush,
1463 /* Description: Flush the stream */
1465 { "eof",
1466 NULL,
1467 aio_cmd_eof,
1470 /* Description: Returns 1 if stream is at eof */
1472 { "close",
1473 "?r(ead)|w(rite)?",
1474 aio_cmd_close,
1477 JIM_MODFLAG_FULLARGV,
1478 /* Description: Closes the stream. */
1480 { "seek",
1481 "offset ?start|current|end",
1482 aio_cmd_seek,
1485 /* Description: Seeks in the stream (default 'current') */
1487 { "tell",
1488 NULL,
1489 aio_cmd_tell,
1492 /* Description: Returns the current seek position */
1494 { "filename",
1495 NULL,
1496 aio_cmd_filename,
1499 /* Description: Returns the original filename */
1501 #ifdef O_NDELAY
1502 { "ndelay",
1503 "?0|1?",
1504 aio_cmd_ndelay,
1507 /* Description: Set O_NDELAY (if arg). Returns current/new setting. */
1509 #endif
1510 #ifdef HAVE_FSYNC
1511 { "sync",
1512 NULL,
1513 aio_cmd_sync,
1516 /* Description: Flush and fsync() the stream */
1518 #endif
1519 { "buffering",
1520 "none|line|full",
1521 aio_cmd_buffering,
1524 /* Description: Sets buffering */
1526 #ifdef jim_ext_eventloop
1527 { "readable",
1528 "?readable-script?",
1529 aio_cmd_readable,
1532 /* Description: Returns script, or invoke readable-script when readable, {} to remove */
1534 { "writable",
1535 "?writable-script?",
1536 aio_cmd_writable,
1539 /* Description: Returns script, or invoke writable-script when writable, {} to remove */
1541 { "onexception",
1542 "?exception-script?",
1543 aio_cmd_onexception,
1546 /* Description: Returns script, or invoke exception-script when oob data, {} to remove */
1548 #endif
1549 #if !defined(JIM_BOOTSTRAP)
1550 #if defined(JIM_SSL)
1551 { "ssl",
1552 "?-server cert priv?",
1553 aio_cmd_ssl,
1556 JIM_MODFLAG_FULLARGV
1557 /* Description: Wraps a stream socket with SSL/TLS and returns a new channel */
1559 { "verify",
1560 NULL,
1561 aio_cmd_verify,
1564 /* Description: Verifies the certificate of a SSL/TLS channel */
1566 #endif
1567 #if defined(HAVE_STRUCT_FLOCK)
1568 { "lock",
1569 NULL,
1570 aio_cmd_lock,
1573 /* Description: Attempt to get a lock. */
1575 { "unlock",
1576 NULL,
1577 aio_cmd_unlock,
1580 /* Description: Relase a lock. */
1582 #endif
1583 #if defined(HAVE_TERMIOS_H)
1584 { "tty",
1585 "?baud rate? ?data bits? ?stop bits? ?parity even|odd|none? ?handshake xonxoff|rtscts|none? ?input raw|cooked? ?output raw|cooked? ?vmin n? ?vtime n?",
1586 aio_cmd_tty,
1589 /* Description: Get or set tty settings - valid only on a tty */
1591 #endif
1592 #endif /* JIM_BOOTSTRAP */
1593 { NULL }
1596 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1598 return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, aio_command_table, argc, argv), argc, argv);
1601 static int JimAioOpenCommand(Jim_Interp *interp, int argc,
1602 Jim_Obj *const *argv)
1604 const char *mode;
1606 if (argc != 2 && argc != 3) {
1607 Jim_WrongNumArgs(interp, 1, argv, "filename ?mode?");
1608 return JIM_ERR;
1611 mode = (argc == 3) ? Jim_String(argv[2]) : "r";
1613 #ifdef jim_ext_tclcompat
1615 const char *filename = Jim_String(argv[1]);
1617 /* If the filename starts with '|', use popen instead */
1618 if (*filename == '|') {
1619 Jim_Obj *evalObj[3];
1621 evalObj[0] = Jim_NewStringObj(interp, "::popen", -1);
1622 evalObj[1] = Jim_NewStringObj(interp, filename + 1, -1);
1623 evalObj[2] = Jim_NewStringObj(interp, mode, -1);
1625 return Jim_EvalObjVector(interp, 3, evalObj);
1628 #endif
1629 return JimMakeChannel(interp, NULL, -1, argv[1], "aio.handle%ld", 0, mode) ? JIM_OK : JIM_ERR;
1632 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1633 static void JimAioSslContextDelProc(struct Jim_Interp *interp, void *privData)
1635 SSL_CTX_free((SSL_CTX *)privData);
1636 ERR_free_strings();
1639 static SSL_CTX *JimAioSslCtx(Jim_Interp *interp)
1641 SSL_CTX *ssl_ctx = (SSL_CTX *)Jim_GetAssocData(interp, "ssl_ctx");
1642 if (ssl_ctx == NULL) {
1643 SSL_load_error_strings();
1644 SSL_library_init();
1645 ssl_ctx = SSL_CTX_new(TLSv1_2_method());
1646 if (ssl_ctx && SSL_CTX_set_default_verify_paths(ssl_ctx)) {
1647 SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
1648 Jim_SetAssocData(interp, "ssl_ctx", JimAioSslContextDelProc, ssl_ctx);
1649 } else {
1650 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1653 return ssl_ctx;
1655 #endif /* JIM_BOOTSTRAP */
1658 * Creates a channel for fh/fd/filename.
1660 * If fh is not NULL, uses that as the channel (and sets AIO_KEEPOPEN).
1661 * Otherwise, if fd is >= 0, uses that as the channel.
1662 * Otherwise opens 'filename' with mode 'mode'.
1664 * hdlfmt is a sprintf format for the filehandle. Anything with %ld at the end will do.
1665 * mode is used for open or fdopen.
1667 * Creates the command and sets the name as the current result.
1668 * Returns the AioFile pointer on sucess or NULL on failure.
1670 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
1671 const char *hdlfmt, int family, const char *mode)
1673 AioFile *af;
1674 char buf[AIO_CMD_LEN];
1675 int openFlags = 0;
1677 snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
1679 if (fh) {
1680 openFlags = AIO_KEEPOPEN;
1683 snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
1684 if (!filename) {
1685 filename = Jim_NewStringObj(interp, buf, -1);
1688 Jim_IncrRefCount(filename);
1690 if (fh == NULL) {
1691 if (fd >= 0) {
1692 #ifndef JIM_ANSIC
1693 fh = fdopen(fd, mode);
1694 #endif
1696 else
1697 fh = fopen(Jim_String(filename), mode);
1699 if (fh == NULL) {
1700 JimAioSetError(interp, filename);
1701 #ifndef JIM_ANSIC
1702 if (fd >= 0) {
1703 close(fd);
1705 #endif
1706 Jim_DecrRefCount(interp, filename);
1707 return NULL;
1711 /* Create the file command */
1712 af = Jim_Alloc(sizeof(*af));
1713 memset(af, 0, sizeof(*af));
1714 af->fp = fh;
1715 af->filename = filename;
1716 af->openFlags = openFlags;
1717 #ifndef JIM_ANSIC
1718 af->fd = fileno(fh);
1719 #ifdef FD_CLOEXEC
1720 if ((openFlags & AIO_KEEPOPEN) == 0) {
1721 (void)fcntl(af->fd, F_SETFD, FD_CLOEXEC);
1723 #endif
1724 #endif
1725 af->addr_family = family;
1726 af->fops = &stdio_fops;
1727 af->ssl = NULL;
1729 Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc);
1731 /* Note that the command must use the global namespace, even if
1732 * the current namespace is something different
1734 Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, Jim_NewStringObj(interp, buf, -1)));
1736 return af;
1739 #if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && defined(HAVE_SYS_UN_H))
1741 * Create a pair of channels. e.g. from pipe() or socketpair()
1743 static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename,
1744 const char *hdlfmt, int family, const char *mode[2])
1746 if (JimMakeChannel(interp, NULL, p[0], filename, hdlfmt, family, mode[0])) {
1747 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
1748 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
1749 if (JimMakeChannel(interp, NULL, p[1], filename, hdlfmt, family, mode[1])) {
1750 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
1751 Jim_SetResult(interp, objPtr);
1752 return JIM_OK;
1756 /* Can only be here if fdopen() failed */
1757 close(p[0]);
1758 close(p[1]);
1759 JimAioSetError(interp, NULL);
1760 return JIM_ERR;
1762 #endif
1764 #ifdef HAVE_PIPE
1765 static int JimAioPipeCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1767 int p[2];
1768 static const char *mode[2] = { "r", "w" };
1770 if (argc != 1) {
1771 Jim_WrongNumArgs(interp, 1, argv, "");
1772 return JIM_ERR;
1775 if (pipe(p) != 0) {
1776 JimAioSetError(interp, NULL);
1777 return JIM_ERR;
1780 return JimMakeChannelPair(interp, p, argv[0], "aio.pipe%ld", 0, mode);
1782 #endif
1784 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
1786 static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1788 const char *hdlfmt = "aio.unknown%ld";
1789 const char *socktypes[] = {
1790 "unix",
1791 "unix.server",
1792 "dgram",
1793 "dgram.server",
1794 "stream",
1795 "stream.server",
1796 "pipe",
1797 "pair",
1798 NULL
1800 enum
1802 SOCK_UNIX,
1803 SOCK_UNIX_SERVER,
1804 SOCK_DGRAM_CLIENT,
1805 SOCK_DGRAM_SERVER,
1806 SOCK_STREAM_CLIENT,
1807 SOCK_STREAM_SERVER,
1808 SOCK_STREAM_PIPE,
1809 SOCK_STREAM_SOCKETPAIR,
1811 int socktype;
1812 int sock;
1813 const char *hostportarg = NULL;
1814 int res;
1815 int on = 1;
1816 const char *mode = "r+";
1817 int family = PF_INET;
1818 Jim_Obj *argv0 = argv[0];
1819 int ipv6 = 0;
1821 if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-ipv6")) {
1822 if (!IPV6) {
1823 Jim_SetResultString(interp, "ipv6 not supported", -1);
1824 return JIM_ERR;
1826 ipv6 = 1;
1827 family = PF_INET6;
1829 argc -= ipv6;
1830 argv += ipv6;
1832 if (argc < 2) {
1833 wrongargs:
1834 Jim_WrongNumArgs(interp, 1, &argv0, "?-ipv6? type ?address?");
1835 return JIM_ERR;
1838 if (Jim_GetEnum(interp, argv[1], socktypes, &socktype, "socket type", JIM_ERRMSG) != JIM_OK)
1839 return Jim_CheckShowCommands(interp, argv[1], socktypes);
1841 Jim_SetEmptyResult(interp);
1843 hdlfmt = "aio.sock%ld";
1845 if (argc > 2) {
1846 hostportarg = Jim_String(argv[2]);
1849 switch (socktype) {
1850 case SOCK_DGRAM_CLIENT:
1851 if (argc == 2) {
1852 /* No address, so an unconnected dgram socket */
1853 sock = socket(family, SOCK_DGRAM, 0);
1854 if (sock < 0) {
1855 JimAioSetError(interp, NULL);
1856 return JIM_ERR;
1858 break;
1860 /* fall through */
1861 case SOCK_STREAM_CLIENT:
1863 union sockaddr_any sa;
1864 int salen;
1866 if (argc != 3) {
1867 goto wrongargs;
1870 if (ipv6) {
1871 if (JimParseIPv6Address(interp, hostportarg, &sa, &salen) != JIM_OK) {
1872 return JIM_ERR;
1875 else if (JimParseIpAddress(interp, hostportarg, &sa, &salen) != JIM_OK) {
1876 return JIM_ERR;
1878 sock = socket(family, (socktype == SOCK_DGRAM_CLIENT) ? SOCK_DGRAM : SOCK_STREAM, 0);
1879 if (sock < 0) {
1880 JimAioSetError(interp, NULL);
1881 return JIM_ERR;
1883 res = connect(sock, &sa.sa, salen);
1884 if (res) {
1885 JimAioSetError(interp, argv[2]);
1886 close(sock);
1887 return JIM_ERR;
1890 break;
1892 case SOCK_STREAM_SERVER:
1893 case SOCK_DGRAM_SERVER:
1895 union sockaddr_any sa;
1896 int salen;
1898 if (argc != 3) {
1899 goto wrongargs;
1902 if (ipv6) {
1903 if (JimParseIPv6Address(interp, hostportarg, &sa, &salen) != JIM_OK) {
1904 return JIM_ERR;
1907 else if (JimParseIpAddress(interp, hostportarg, &sa, &salen) != JIM_OK) {
1908 return JIM_ERR;
1910 sock = socket(family, (socktype == SOCK_DGRAM_SERVER) ? SOCK_DGRAM : SOCK_STREAM, 0);
1911 if (sock < 0) {
1912 JimAioSetError(interp, NULL);
1913 return JIM_ERR;
1916 /* Enable address reuse */
1917 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
1919 res = bind(sock, &sa.sa, salen);
1920 if (res) {
1921 JimAioSetError(interp, argv[2]);
1922 close(sock);
1923 return JIM_ERR;
1925 if (socktype == SOCK_STREAM_SERVER) {
1926 res = listen(sock, 5);
1927 if (res) {
1928 JimAioSetError(interp, NULL);
1929 close(sock);
1930 return JIM_ERR;
1933 hdlfmt = "aio.socksrv%ld";
1935 break;
1937 #ifdef HAVE_SYS_UN_H
1938 case SOCK_UNIX:
1940 struct sockaddr_un sa;
1941 socklen_t len;
1943 if (argc != 3 || ipv6) {
1944 goto wrongargs;
1947 if (JimParseDomainAddress(interp, hostportarg, &sa) != JIM_OK) {
1948 JimAioSetError(interp, argv[2]);
1949 return JIM_ERR;
1951 family = PF_UNIX;
1952 sock = socket(PF_UNIX, SOCK_STREAM, 0);
1953 if (sock < 0) {
1954 JimAioSetError(interp, NULL);
1955 return JIM_ERR;
1957 len = strlen(sa.sun_path) + 1 + sizeof(sa.sun_family);
1958 res = connect(sock, (struct sockaddr *)&sa, len);
1959 if (res) {
1960 JimAioSetError(interp, argv[2]);
1961 close(sock);
1962 return JIM_ERR;
1964 hdlfmt = "aio.sockunix%ld";
1965 break;
1968 case SOCK_UNIX_SERVER:
1970 struct sockaddr_un sa;
1971 socklen_t len;
1973 if (argc != 3 || ipv6) {
1974 goto wrongargs;
1977 if (JimParseDomainAddress(interp, hostportarg, &sa) != JIM_OK) {
1978 JimAioSetError(interp, argv[2]);
1979 return JIM_ERR;
1981 family = PF_UNIX;
1982 sock = socket(PF_UNIX, SOCK_STREAM, 0);
1983 if (sock < 0) {
1984 JimAioSetError(interp, NULL);
1985 return JIM_ERR;
1987 len = strlen(sa.sun_path) + 1 + sizeof(sa.sun_family);
1988 res = bind(sock, (struct sockaddr *)&sa, len);
1989 if (res) {
1990 JimAioSetError(interp, argv[2]);
1991 close(sock);
1992 return JIM_ERR;
1994 res = listen(sock, 5);
1995 if (res) {
1996 JimAioSetError(interp, NULL);
1997 close(sock);
1998 return JIM_ERR;
2000 hdlfmt = "aio.sockunixsrv%ld";
2001 break;
2003 #endif
2005 #if defined(HAVE_SOCKETPAIR) && defined(HAVE_SYS_UN_H)
2006 case SOCK_STREAM_SOCKETPAIR:
2008 int p[2];
2009 static const char *mode[2] = { "r+", "r+" };
2011 if (argc != 2 || ipv6) {
2012 goto wrongargs;
2015 if (socketpair(PF_UNIX, SOCK_STREAM, 0, p) < 0) {
2016 JimAioSetError(interp, NULL);
2017 return JIM_ERR;
2019 return JimMakeChannelPair(interp, p, argv[1], "aio.sockpair%ld", PF_UNIX, mode);
2021 break;
2022 #endif
2024 #if defined(HAVE_PIPE)
2025 case SOCK_STREAM_PIPE:
2026 if (argc != 2 || ipv6) {
2027 goto wrongargs;
2029 return JimAioPipeCommand(interp, 1, &argv[1]);
2030 #endif
2032 default:
2033 Jim_SetResultString(interp, "Unsupported socket type", -1);
2034 return JIM_ERR;
2037 return JimMakeChannel(interp, NULL, sock, argv[1], hdlfmt, family, mode) ? JIM_OK : JIM_ERR;
2039 #endif /* JIM_BOOTSTRAP */
2041 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
2042 static int JimAioLoadSSLCertsCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2044 SSL_CTX *ssl_ctx;
2046 if (argc != 2) {
2047 Jim_WrongNumArgs(interp, 1, argv, "dir");
2048 return JIM_ERR;
2051 ssl_ctx = JimAioSslCtx(interp);
2052 if (!ssl_ctx) {
2053 return JIM_ERR;
2055 if (SSL_CTX_load_verify_locations(ssl_ctx, NULL, Jim_String(argv[1])) == 1) {
2056 return JIM_OK;
2058 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
2059 return JIM_ERR;
2061 #endif /* JIM_BOOTSTRAP */
2063 int Jim_aioInit(Jim_Interp *interp)
2065 if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG))
2066 return JIM_ERR;
2068 #if defined(JIM_SSL)
2069 Jim_CreateCommand(interp, "load_ssl_certs", JimAioLoadSSLCertsCommand, NULL, NULL);
2070 #endif
2072 Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL);
2073 #ifdef HAVE_SOCKETS
2074 Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL);
2075 #endif
2076 #ifdef HAVE_PIPE
2077 Jim_CreateCommand(interp, "pipe", JimAioPipeCommand, NULL, NULL);
2078 #endif
2080 /* Create filehandles for stdin, stdout and stderr */
2081 JimMakeChannel(interp, stdin, -1, NULL, "stdin", 0, "r");
2082 JimMakeChannel(interp, stdout, -1, NULL, "stdout", 0, "w");
2083 JimMakeChannel(interp, stderr, -1, NULL, "stderr", 0, "w");
2085 return JIM_OK;