tests: Try to fix exec2-3.2 on Windows
[jimtcl.git] / jim-aio.c
blobe72cce5f1d43d9e238318fa64512a266f1f72561
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 #define JimCheckStreamError(interp, af) af->fops->error(af)
114 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
115 union sockaddr_any {
116 struct sockaddr sa;
117 struct sockaddr_in sin;
118 #if IPV6
119 struct sockaddr_in6 sin6;
120 #endif
123 #ifndef HAVE_INET_NTOP
124 const char *inet_ntop(int af, const void *src, char *dst, int size)
126 if (af != PF_INET) {
127 return NULL;
129 snprintf(dst, size, "%s", inet_ntoa(((struct sockaddr_in *)src)->sin_addr));
130 return dst;
132 #endif
133 #endif /* JIM_BOOTSTRAP */
135 struct AioFile;
137 typedef struct {
138 int (*writer)(struct AioFile *af, const char *buf, int len);
139 int (*reader)(struct AioFile *af, char *buf, int len);
140 const char *(*getline)(struct AioFile *af, char *buf, int len);
141 int (*error)(const struct AioFile *af);
142 const char *(*strerror)(struct AioFile *af);
143 int (*verify)(struct AioFile *af);
144 } JimAioFopsType;
146 typedef struct AioFile
148 FILE *fp;
149 Jim_Obj *filename;
150 int type;
151 int openFlags; /* AIO_KEEPOPEN? keep FILE* */
152 int fd;
153 Jim_Obj *rEvent;
154 Jim_Obj *wEvent;
155 Jim_Obj *eEvent;
156 int addr_family;
157 void *ssl;
158 const JimAioFopsType *fops;
159 } AioFile;
161 static int stdio_writer(struct AioFile *af, const char *buf, int len)
163 return fwrite(buf, 1, len, af->fp);
166 static int stdio_reader(struct AioFile *af, char *buf, int len)
168 return fread(buf, 1, len, af->fp);
171 static const char *stdio_getline(struct AioFile *af, char *buf, int len)
173 return fgets(buf, len, af->fp);
176 static int stdio_error(const AioFile *af)
178 if (!ferror(af->fp)) {
179 return JIM_OK;
181 clearerr(af->fp);
182 /* EAGAIN and similar are not error conditions. Just treat them like eof */
183 if (feof(af->fp) || errno == EAGAIN || errno == EINTR) {
184 return JIM_OK;
186 #ifdef ECONNRESET
187 if (errno == ECONNRESET) {
188 return JIM_OK;
190 #endif
191 #ifdef ECONNABORTED
192 if (errno == ECONNABORTED) {
193 return JIM_OK;
195 #endif
196 return JIM_ERR;
199 static const char *stdio_strerror(struct AioFile *af)
201 return strerror(errno);
204 static const JimAioFopsType stdio_fops = {
205 stdio_writer,
206 stdio_reader,
207 stdio_getline,
208 stdio_error,
209 stdio_strerror,
210 NULL
213 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
215 static SSL_CTX *JimAioSslCtx(Jim_Interp *interp);
217 static int ssl_writer(struct AioFile *af, const char *buf, int len)
219 return SSL_write(af->ssl, buf, len);
222 static int ssl_reader(struct AioFile *af, char *buf, int len)
224 return SSL_read(af->ssl, buf, len);
227 static const char *ssl_getline(struct AioFile *af, char *buf, int len)
229 int i;
230 for (i = 0; i < len + 1; i++) {
231 if (SSL_read(af->ssl, &buf[i], 1) != 1) {
232 if (i == 0) {
233 return NULL;
235 break;
237 if (buf[i] == '\n') {
238 break;
241 buf[i] = '\0';
242 return buf;
245 static int ssl_error(const struct AioFile *af)
247 if (ERR_peek_error() == 0) {
248 return JIM_OK;
251 return JIM_ERR;
254 static const char *ssl_strerror(struct AioFile *af)
256 int err = ERR_get_error();
258 if (err) {
259 return ERR_error_string(err, NULL);
262 /* should not happen */
263 return "unknown SSL error";
266 static int ssl_verify(struct AioFile *af)
268 X509 *cert;
270 cert = SSL_get_peer_certificate(af->ssl);
271 if (!cert) {
272 return JIM_ERR;
274 X509_free(cert);
276 if (SSL_get_verify_result(af->ssl) == X509_V_OK) {
277 return JIM_OK;
280 return JIM_ERR;
283 static const JimAioFopsType ssl_fops = {
284 ssl_writer,
285 ssl_reader,
286 ssl_getline,
287 ssl_error,
288 ssl_strerror,
289 ssl_verify
291 #endif /* JIM_BOOTSTRAP */
293 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
294 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
295 const char *hdlfmt, int family, const char *mode);
297 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
298 static int JimParseIPv6Address(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, int *salen)
300 #if IPV6
302 * An IPv6 addr/port looks like:
303 * [::1]
304 * [::1]:2000
305 * [fe80::223:6cff:fe95:bdc0%en1]:2000
306 * [::]:2000
307 * 2000
309 * Note that the "any" address is ::, which is the same as when no address is specified.
311 char *sthost = NULL;
312 const char *stport;
313 int ret = JIM_OK;
314 struct addrinfo req;
315 struct addrinfo *ai;
317 stport = strrchr(hostport, ':');
318 if (!stport) {
319 /* No : so, the whole thing is the port */
320 stport = hostport;
321 hostport = "::";
322 sthost = Jim_StrDup(hostport);
324 else {
325 stport++;
328 if (*hostport == '[') {
329 /* This is a numeric ipv6 address */
330 char *pt = strchr(++hostport, ']');
331 if (pt) {
332 sthost = Jim_StrDupLen(hostport, pt - hostport);
336 if (!sthost) {
337 sthost = Jim_StrDupLen(hostport, stport - hostport - 1);
340 memset(&req, '\0', sizeof(req));
341 req.ai_family = PF_INET6;
343 if (getaddrinfo(sthost, NULL, &req, &ai)) {
344 Jim_SetResultFormatted(interp, "Not a valid address: %s", hostport);
345 ret = JIM_ERR;
347 else {
348 memcpy(&sa->sin, ai->ai_addr, ai->ai_addrlen);
349 *salen = ai->ai_addrlen;
351 sa->sin.sin_port = htons(atoi(stport));
353 freeaddrinfo(ai);
355 Jim_Free(sthost);
357 return ret;
358 #else
359 Jim_SetResultString(interp, "ipv6 not supported", -1);
360 return JIM_ERR;
361 #endif
364 static int JimParseIpAddress(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, int *salen)
366 /* An IPv4 addr/port looks like:
367 * 192.168.1.5
368 * 192.168.1.5:2000
369 * 2000
371 * If the address is missing, INADDR_ANY is used.
372 * If the port is missing, 0 is used (only useful for server sockets).
374 char *sthost = NULL;
375 const char *stport;
376 int ret = JIM_OK;
378 stport = strrchr(hostport, ':');
379 if (!stport) {
380 /* No : so, the whole thing is the port */
381 stport = hostport;
382 sthost = Jim_StrDup("0.0.0.0");
384 else {
385 sthost = Jim_StrDupLen(hostport, stport - hostport);
386 stport++;
390 #ifdef HAVE_GETADDRINFO
391 struct addrinfo req;
392 struct addrinfo *ai;
393 memset(&req, '\0', sizeof(req));
394 req.ai_family = PF_INET;
396 if (getaddrinfo(sthost, NULL, &req, &ai)) {
397 ret = JIM_ERR;
399 else {
400 memcpy(&sa->sin, ai->ai_addr, ai->ai_addrlen);
401 *salen = ai->ai_addrlen;
402 freeaddrinfo(ai);
404 #else
405 struct hostent *he;
407 ret = JIM_ERR;
409 if ((he = gethostbyname(sthost)) != NULL) {
410 if (he->h_length == sizeof(sa->sin.sin_addr)) {
411 *salen = sizeof(sa->sin);
412 sa->sin.sin_family= he->h_addrtype;
413 memcpy(&sa->sin.sin_addr, he->h_addr, he->h_length); /* set address */
414 ret = JIM_OK;
417 #endif
419 sa->sin.sin_port = htons(atoi(stport));
421 Jim_Free(sthost);
423 if (ret != JIM_OK) {
424 Jim_SetResultFormatted(interp, "Not a valid address: %s", hostport);
427 return ret;
430 #ifdef HAVE_SYS_UN_H
431 static int JimParseDomainAddress(Jim_Interp *interp, const char *path, struct sockaddr_un *sa)
433 sa->sun_family = PF_UNIX;
434 snprintf(sa->sun_path, sizeof(sa->sun_path), "%s", path);
436 return JIM_OK;
438 #endif
441 * Format that address in 'sa' as a string and store in variable 'varObjPtr'
443 static int JimFormatIpAddress(Jim_Interp *interp, Jim_Obj *varObjPtr, const union sockaddr_any *sa)
445 /* INET6_ADDRSTRLEN is 46. Add some for [] and port */
446 char addrbuf[60];
448 #if IPV6
449 if (sa->sa.sa_family == PF_INET6) {
450 addrbuf[0] = '[';
451 /* Allow 9 for []:65535\0 */
452 inet_ntop(sa->sa.sa_family, &sa->sin6.sin6_addr, addrbuf + 1, sizeof(addrbuf) - 9);
453 snprintf(addrbuf + strlen(addrbuf), 8, "]:%d", ntohs(sa->sin.sin_port));
455 else
456 #endif
457 if (sa->sa.sa_family == PF_INET) {
458 /* Allow 7 for :65535\0 */
459 inet_ntop(sa->sa.sa_family, &sa->sin.sin_addr, addrbuf, sizeof(addrbuf) - 7);
460 snprintf(addrbuf + strlen(addrbuf), 7, ":%d", ntohs(sa->sin.sin_port));
462 else {
463 /* recvfrom still works on unix domain sockets, etc */
464 addrbuf[0] = 0;
467 return Jim_SetVariable(interp, varObjPtr, Jim_NewStringObj(interp, addrbuf, -1));
470 #endif /* JIM_BOOTSTRAP */
472 static const char *JimAioErrorString(AioFile *af)
474 if (af && af->fops)
475 return af->fops->strerror(af);
477 return strerror(errno);
480 static void JimAioSetError(Jim_Interp *interp, Jim_Obj *name)
482 AioFile *af = Jim_CmdPrivData(interp);
484 if (name) {
485 Jim_SetResultFormatted(interp, "%#s: %s", name, JimAioErrorString(af));
487 else {
488 Jim_SetResultString(interp, JimAioErrorString(af), -1);
492 static void JimAioDelProc(Jim_Interp *interp, void *privData)
494 AioFile *af = privData;
496 JIM_NOTUSED(interp);
498 Jim_DecrRefCount(interp, af->filename);
500 #ifdef jim_ext_eventloop
501 /* remove all existing EventHandlers */
502 Jim_DeleteFileHandler(interp, af->fd, JIM_EVENT_READABLE | JIM_EVENT_WRITABLE | JIM_EVENT_EXCEPTION);
503 #endif
505 #if defined(JIM_SSL)
506 if (af->ssl != NULL) {
507 SSL_free(af->ssl);
509 #endif
510 if (!(af->openFlags & AIO_KEEPOPEN)) {
511 fclose(af->fp);
514 Jim_Free(af);
517 static int aio_cmd_read(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
519 AioFile *af = Jim_CmdPrivData(interp);
520 char buf[AIO_BUF_LEN];
521 Jim_Obj *objPtr;
522 int nonewline = 0;
523 jim_wide neededLen = -1; /* -1 is "read as much as possible" */
525 if (argc && Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
526 nonewline = 1;
527 argv++;
528 argc--;
530 if (argc == 1) {
531 if (Jim_GetWide(interp, argv[0], &neededLen) != JIM_OK)
532 return JIM_ERR;
533 if (neededLen < 0) {
534 Jim_SetResultString(interp, "invalid parameter: negative len", -1);
535 return JIM_ERR;
538 else if (argc) {
539 return -1;
541 objPtr = Jim_NewStringObj(interp, NULL, 0);
542 while (neededLen != 0) {
543 int retval;
544 int readlen;
546 if (neededLen == -1) {
547 readlen = AIO_BUF_LEN;
549 else {
550 readlen = (neededLen > AIO_BUF_LEN ? AIO_BUF_LEN : neededLen);
552 retval = af->fops->reader(af, buf, readlen);
553 if (retval > 0) {
554 Jim_AppendString(interp, objPtr, buf, retval);
555 if (neededLen != -1) {
556 neededLen -= retval;
559 if (retval != readlen)
560 break;
562 /* Check for error conditions */
563 if (JimCheckStreamError(interp, af)) {
564 Jim_FreeNewObj(interp, objPtr);
565 return JIM_ERR;
567 if (nonewline) {
568 int len;
569 const char *s = Jim_GetString(objPtr, &len);
571 if (len > 0 && s[len - 1] == '\n') {
572 objPtr->length--;
573 objPtr->bytes[objPtr->length] = '\0';
576 Jim_SetResult(interp, objPtr);
577 return JIM_OK;
580 AioFile *Jim_AioFile(Jim_Interp *interp, Jim_Obj *command)
582 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, command, JIM_ERRMSG);
584 /* XXX: There ought to be a supported API for this */
585 if (cmdPtr && !cmdPtr->isproc && cmdPtr->u.native.cmdProc == JimAioSubCmdProc) {
586 return (AioFile *) cmdPtr->u.native.privData;
588 Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", command);
589 return NULL;
592 FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command)
594 AioFile *af;
596 af = Jim_AioFile(interp, command);
597 if (af == NULL) {
598 return NULL;
601 return af->fp;
604 static int aio_cmd_getfd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
606 AioFile *af = Jim_CmdPrivData(interp);
608 fflush(af->fp);
609 Jim_SetResultInt(interp, fileno(af->fp));
611 return JIM_OK;
614 static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
616 AioFile *af = Jim_CmdPrivData(interp);
617 jim_wide count = 0;
618 jim_wide maxlen = JIM_WIDE_MAX;
619 AioFile *outf = Jim_AioFile(interp, argv[0]);
621 if (outf == NULL) {
622 return JIM_ERR;
625 if (argc == 2) {
626 if (Jim_GetWide(interp, argv[1], &maxlen) != JIM_OK) {
627 return JIM_ERR;
631 while (count < maxlen) {
632 char ch;
634 if (af->fops->reader(af, &ch, 1) != 1) {
635 break;
637 if (outf->fops->writer(outf, &ch, 1) != 1) {
638 break;
640 count++;
643 if (JimCheckStreamError(interp, af) || JimCheckStreamError(interp, outf)) {
644 return JIM_ERR;
647 Jim_SetResultInt(interp, count);
649 return JIM_OK;
652 static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
654 AioFile *af = Jim_CmdPrivData(interp);
655 char buf[AIO_BUF_LEN];
656 Jim_Obj *objPtr;
657 int len;
659 errno = 0;
661 objPtr = Jim_NewStringObj(interp, NULL, 0);
662 while (1) {
663 buf[AIO_BUF_LEN - 1] = '_';
665 if (af->fops->getline(af, buf, AIO_BUF_LEN) == NULL)
666 break;
668 if (buf[AIO_BUF_LEN - 1] == '\0' && buf[AIO_BUF_LEN - 2] != '\n') {
669 Jim_AppendString(interp, objPtr, buf, AIO_BUF_LEN - 1);
671 else {
672 len = strlen(buf);
674 if (len && (buf[len - 1] == '\n')) {
675 /* strip "\n" */
676 len--;
679 Jim_AppendString(interp, objPtr, buf, len);
680 break;
684 if (JimCheckStreamError(interp, af)) {
685 /* I/O error */
686 Jim_FreeNewObj(interp, objPtr);
687 return JIM_ERR;
690 if (argc) {
691 if (Jim_SetVariable(interp, argv[0], objPtr) != JIM_OK) {
692 Jim_FreeNewObj(interp, objPtr);
693 return JIM_ERR;
696 len = Jim_Length(objPtr);
698 if (len == 0 && feof(af->fp)) {
699 /* On EOF returns -1 if varName was specified */
700 len = -1;
702 Jim_SetResultInt(interp, len);
704 else {
705 Jim_SetResult(interp, objPtr);
707 return JIM_OK;
710 static int aio_cmd_puts(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
712 AioFile *af = Jim_CmdPrivData(interp);
713 int wlen;
714 const char *wdata;
715 Jim_Obj *strObj;
717 if (argc == 2) {
718 if (!Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
719 return -1;
721 strObj = argv[1];
723 else {
724 strObj = argv[0];
727 wdata = Jim_GetString(strObj, &wlen);
728 if (af->fops->writer(af, wdata, wlen) == wlen) {
729 if (argc == 2 || af->fops->writer(af, "\n", 1) == 1) {
730 return JIM_OK;
733 JimAioSetError(interp, af->filename);
734 return JIM_ERR;
737 static int aio_cmd_isatty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
739 #ifdef HAVE_ISATTY
740 AioFile *af = Jim_CmdPrivData(interp);
741 Jim_SetResultInt(interp, isatty(fileno(af->fp)));
742 #else
743 Jim_SetResultInt(interp, 0);
744 #endif
746 return JIM_OK;
749 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
750 static int aio_cmd_recvfrom(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
752 AioFile *af = Jim_CmdPrivData(interp);
753 char *buf;
754 union sockaddr_any sa;
755 long len;
756 socklen_t salen = sizeof(sa);
757 int rlen;
759 if (Jim_GetLong(interp, argv[0], &len) != JIM_OK) {
760 return JIM_ERR;
763 buf = Jim_Alloc(len + 1);
765 rlen = recvfrom(fileno(af->fp), buf, len, 0, &sa.sa, &salen);
766 if (rlen < 0) {
767 Jim_Free(buf);
768 JimAioSetError(interp, NULL);
769 return JIM_ERR;
771 buf[rlen] = 0;
772 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, rlen));
774 if (argc > 1) {
775 return JimFormatIpAddress(interp, argv[1], &sa);
778 return JIM_OK;
782 static int aio_cmd_sendto(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
784 AioFile *af = Jim_CmdPrivData(interp);
785 int wlen;
786 int len;
787 const char *wdata;
788 union sockaddr_any sa;
789 const char *addr = Jim_String(argv[1]);
790 int salen;
792 if (IPV6 && af->addr_family == PF_INET6) {
793 if (JimParseIPv6Address(interp, addr, &sa, &salen) != JIM_OK) {
794 return JIM_ERR;
797 else if (JimParseIpAddress(interp, addr, &sa, &salen) != JIM_OK) {
798 return JIM_ERR;
800 wdata = Jim_GetString(argv[0], &wlen);
802 /* Note that we don't validate the socket type. Rely on sendto() failing if appropriate */
803 len = sendto(fileno(af->fp), wdata, wlen, 0, &sa.sa, salen);
804 if (len < 0) {
805 JimAioSetError(interp, NULL);
806 return JIM_ERR;
808 Jim_SetResultInt(interp, len);
809 return JIM_OK;
812 static int aio_cmd_accept(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
814 AioFile *af = Jim_CmdPrivData(interp);
815 int sock;
816 union sockaddr_any sa;
817 socklen_t addrlen = sizeof(sa);
819 sock = accept(af->fd, &sa.sa, &addrlen);
820 if (sock < 0) {
821 JimAioSetError(interp, NULL);
822 return JIM_ERR;
825 if (argc > 0) {
826 if (JimFormatIpAddress(interp, argv[0], &sa) != JIM_OK) {
827 return JIM_ERR;
831 /* Create the file command */
832 return JimMakeChannel(interp, NULL, sock, Jim_NewStringObj(interp, "accept", -1),
833 "aio.sockstream%ld", af->addr_family, "r+") ? JIM_OK : JIM_ERR;
836 static int aio_cmd_listen(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
838 AioFile *af = Jim_CmdPrivData(interp);
839 long backlog;
841 if (Jim_GetLong(interp, argv[0], &backlog) != JIM_OK) {
842 return JIM_ERR;
845 if (listen(af->fd, backlog)) {
846 JimAioSetError(interp, NULL);
847 return JIM_ERR;
850 return JIM_OK;
852 #endif /* JIM_BOOTSTRAP */
854 static int aio_cmd_flush(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
856 AioFile *af = Jim_CmdPrivData(interp);
858 if (fflush(af->fp) == EOF) {
859 JimAioSetError(interp, af->filename);
860 return JIM_ERR;
862 return JIM_OK;
865 static int aio_cmd_eof(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
867 AioFile *af = Jim_CmdPrivData(interp);
869 Jim_SetResultInt(interp, feof(af->fp));
870 return JIM_OK;
873 static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
875 if (argc == 3) {
876 #if defined(HAVE_SOCKETS) && defined(HAVE_SHUTDOWN)
877 static const char * const options[] = { "r", "w", NULL };
878 enum { OPT_R, OPT_W, };
879 int option;
880 AioFile *af = Jim_CmdPrivData(interp);
882 if (Jim_GetEnum(interp, argv[2], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
883 return JIM_ERR;
885 if (shutdown(af->fd, option == OPT_R ? SHUT_RD : SHUT_WR) == 0) {
886 return JIM_OK;
888 JimAioSetError(interp, NULL);
889 #else
890 Jim_SetResultString(interp, "async close not supported", -1);
891 #endif
892 return JIM_ERR;
895 return Jim_DeleteCommand(interp, Jim_String(argv[0]));
898 static int aio_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
900 AioFile *af = Jim_CmdPrivData(interp);
901 int orig = SEEK_SET;
902 jim_wide offset;
904 if (argc == 2) {
905 if (Jim_CompareStringImmediate(interp, argv[1], "start"))
906 orig = SEEK_SET;
907 else if (Jim_CompareStringImmediate(interp, argv[1], "current"))
908 orig = SEEK_CUR;
909 else if (Jim_CompareStringImmediate(interp, argv[1], "end"))
910 orig = SEEK_END;
911 else {
912 return -1;
915 if (Jim_GetWide(interp, argv[0], &offset) != JIM_OK) {
916 return JIM_ERR;
918 if (fseeko(af->fp, offset, orig) == -1) {
919 JimAioSetError(interp, af->filename);
920 return JIM_ERR;
922 return JIM_OK;
925 static int aio_cmd_tell(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
927 AioFile *af = Jim_CmdPrivData(interp);
929 Jim_SetResultInt(interp, ftello(af->fp));
930 return JIM_OK;
933 static int aio_cmd_filename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
935 AioFile *af = Jim_CmdPrivData(interp);
937 Jim_SetResult(interp, af->filename);
938 return JIM_OK;
941 #ifdef O_NDELAY
942 static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
944 AioFile *af = Jim_CmdPrivData(interp);
946 int fmode = fcntl(af->fd, F_GETFL);
948 if (argc) {
949 long nb;
951 if (Jim_GetLong(interp, argv[0], &nb) != JIM_OK) {
952 return JIM_ERR;
954 if (nb) {
955 fmode |= O_NDELAY;
957 else {
958 fmode &= ~O_NDELAY;
960 (void)fcntl(af->fd, F_SETFL, fmode);
962 Jim_SetResultInt(interp, (fmode & O_NONBLOCK) ? 1 : 0);
963 return JIM_OK;
965 #endif
967 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
968 #define SOCKOPT_BOOL 0
969 #define SOCKOPT_INT 1
970 #define SOCKOPT_TIMEVAL 2 /* not currently supported */
972 static const struct sockopt_def {
973 const char *name;
974 int level;
975 int opt;
976 int type; /* SOCKOPT_xxx */
977 } sockopts[] = {
978 #ifdef SOL_SOCKET
979 #ifdef SO_BROADCAST
980 { "broadcast", SOL_SOCKET, SO_BROADCAST },
981 #endif
982 #ifdef SO_DEBUG
983 { "debug", SOL_SOCKET, SO_DEBUG },
984 #endif
985 #ifdef SO_KEEPALIVE
986 { "keepalive", SOL_SOCKET, SO_KEEPALIVE },
987 #endif
988 #ifdef SO_NOSIGPIPE
989 { "nosigpipe", SOL_SOCKET, SO_NOSIGPIPE },
990 #endif
991 #ifdef SO_OOBINLINE
992 { "oobinline", SOL_SOCKET, SO_OOBINLINE },
993 #endif
994 #ifdef SO_SNDBUF
995 { "sndbuf", SOL_SOCKET, SO_SNDBUF, SOCKOPT_INT },
996 #endif
997 #ifdef SO_RCVBUF
998 { "rcvbuf", SOL_SOCKET, SO_RCVBUF, SOCKOPT_INT },
999 #endif
1000 #if 0 && defined(SO_SNDTIMEO)
1001 { "sndtimeo", SOL_SOCKET, SO_SNDTIMEO, SOCKOPT_TIMEVAL },
1002 #endif
1003 #if 0 && defined(SO_RCVTIMEO)
1004 { "rcvtimeo", SOL_SOCKET, SO_RCVTIMEO, SOCKOPT_TIMEVAL },
1005 #endif
1006 #endif
1007 #ifdef IPPROTO_TCP
1008 #ifdef TCP_NODELAY
1009 { "tcp_nodelay", IPPROTO_TCP, TCP_NODELAY },
1010 #endif
1011 #endif
1014 static int aio_cmd_sockopt(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1016 AioFile *af = Jim_CmdPrivData(interp);
1017 int i;
1019 if (argc == 0) {
1020 Jim_Obj *dictObjPtr = Jim_NewListObj(interp, NULL, 0);
1021 for (i = 0; i < sizeof(sockopts) / sizeof(*sockopts); i++) {
1022 int value = 0;
1023 socklen_t len = sizeof(value);
1024 if (getsockopt(af->fd, sockopts[i].level, sockopts[i].opt, (void *)&value, &len) == 0) {
1025 if (sockopts[i].type == SOCKOPT_BOOL) {
1026 value = !!value;
1028 Jim_ListAppendElement(interp, dictObjPtr, Jim_NewStringObj(interp, sockopts[i].name, -1));
1029 Jim_ListAppendElement(interp, dictObjPtr, Jim_NewIntObj(interp, value));
1032 Jim_SetResult(interp, dictObjPtr);
1033 return JIM_OK;
1035 if (argc == 1) {
1036 return -1;
1039 /* Set an option */
1040 for (i = 0; i < sizeof(sockopts) / sizeof(*sockopts); i++) {
1041 if (strcmp(Jim_String(argv[0]), sockopts[i].name) == 0) {
1042 int on;
1043 if (sockopts[i].type == SOCKOPT_BOOL) {
1044 if (Jim_GetBoolean(interp, argv[1], &on) != JIM_OK) {
1045 return JIM_ERR;
1048 else {
1049 long longval;
1050 if (Jim_GetLong(interp, argv[1], &longval) != JIM_OK) {
1051 return JIM_ERR;
1053 on = longval;
1055 if (setsockopt(af->fd, sockopts[i].level, sockopts[i].opt, (void *)&on, sizeof(on)) < 0) {
1056 Jim_SetResultFormatted(interp, "Failed to set %#s: %s", argv[0], strerror(errno));
1057 return JIM_ERR;
1059 return JIM_OK;
1062 /* Not found */
1063 Jim_SetResultFormatted(interp, "Unknown sockopt %#s", argv[0]);
1064 return JIM_ERR;
1066 #endif /* JIM_BOOTSTRAP */
1068 #ifdef HAVE_FSYNC
1069 static int aio_cmd_sync(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1071 AioFile *af = Jim_CmdPrivData(interp);
1073 fflush(af->fp);
1074 fsync(af->fd);
1075 return JIM_OK;
1077 #endif
1079 static int aio_cmd_buffering(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1081 AioFile *af = Jim_CmdPrivData(interp);
1083 static const char * const options[] = {
1084 "none",
1085 "line",
1086 "full",
1087 NULL
1089 enum
1091 OPT_NONE,
1092 OPT_LINE,
1093 OPT_FULL,
1095 int option;
1097 if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
1098 return JIM_ERR;
1100 switch (option) {
1101 case OPT_NONE:
1102 setvbuf(af->fp, NULL, _IONBF, 0);
1103 break;
1104 case OPT_LINE:
1105 setvbuf(af->fp, NULL, _IOLBF, BUFSIZ);
1106 break;
1107 case OPT_FULL:
1108 setvbuf(af->fp, NULL, _IOFBF, BUFSIZ);
1109 break;
1111 return JIM_OK;
1114 #ifdef jim_ext_eventloop
1115 static void JimAioFileEventFinalizer(Jim_Interp *interp, void *clientData)
1117 Jim_Obj **objPtrPtr = clientData;
1119 Jim_DecrRefCount(interp, *objPtrPtr);
1120 *objPtrPtr = NULL;
1123 static int JimAioFileEventHandler(Jim_Interp *interp, void *clientData, int mask)
1125 Jim_Obj **objPtrPtr = clientData;
1127 return Jim_EvalObjBackground(interp, *objPtrPtr);
1130 static int aio_eventinfo(Jim_Interp *interp, AioFile * af, unsigned mask, Jim_Obj **scriptHandlerObj,
1131 int argc, Jim_Obj * const *argv)
1133 if (argc == 0) {
1134 /* Return current script */
1135 if (*scriptHandlerObj) {
1136 Jim_SetResult(interp, *scriptHandlerObj);
1138 return JIM_OK;
1141 if (*scriptHandlerObj) {
1142 /* Delete old handler */
1143 Jim_DeleteFileHandler(interp, af->fd, mask);
1146 /* Now possibly add the new script(s) */
1147 if (Jim_Length(argv[0]) == 0) {
1148 /* Empty script, so done */
1149 return JIM_OK;
1152 /* A new script to add */
1153 Jim_IncrRefCount(argv[0]);
1154 *scriptHandlerObj = argv[0];
1156 Jim_CreateFileHandler(interp, af->fd, mask,
1157 JimAioFileEventHandler, scriptHandlerObj, JimAioFileEventFinalizer);
1159 return JIM_OK;
1162 static int aio_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1164 AioFile *af = Jim_CmdPrivData(interp);
1166 return aio_eventinfo(interp, af, JIM_EVENT_READABLE, &af->rEvent, argc, argv);
1169 static int aio_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1171 AioFile *af = Jim_CmdPrivData(interp);
1173 return aio_eventinfo(interp, af, JIM_EVENT_WRITABLE, &af->wEvent, argc, argv);
1176 static int aio_cmd_onexception(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1178 AioFile *af = Jim_CmdPrivData(interp);
1180 return aio_eventinfo(interp, af, JIM_EVENT_EXCEPTION, &af->eEvent, argc, argv);
1182 #endif
1184 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1185 static int aio_cmd_ssl(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1187 AioFile *af = Jim_CmdPrivData(interp);
1188 SSL *ssl;
1189 SSL_CTX *ssl_ctx;
1190 int fd, server = 0;
1192 if (argc == 5) {
1193 if (!Jim_CompareStringImmediate(interp, argv[2], "-server")) {
1194 return JIM_ERR;
1196 server = 1;
1198 else if (argc != 2) {
1199 Jim_WrongNumArgs(interp, 2, argv, "?-server cert priv?");
1200 return JIM_ERR;
1203 fd = fileno(af->fp);
1204 #if defined(HAVE_DUP)
1205 fd = dup(fd);
1206 if (fd < 0) {
1207 return JIM_ERR;
1209 #endif
1210 ssl_ctx = JimAioSslCtx(interp);
1211 if (ssl_ctx == NULL) {
1212 return JIM_ERR;
1215 ssl = SSL_new(ssl_ctx);
1216 if (ssl == NULL) {
1217 #if defined(HAVE_DUP)
1218 close(fd);
1219 #endif
1220 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1221 return JIM_ERR;
1224 SSL_set_cipher_list(ssl, "ALL");
1226 if (SSL_set_fd(ssl, fileno(af->fp)) == 0) {
1227 goto out;
1230 if (server) {
1231 if (SSL_use_certificate_file(ssl, Jim_String(argv[3]), SSL_FILETYPE_PEM) != 1) {
1232 goto out;
1235 if (SSL_use_PrivateKey_file(ssl, Jim_String(argv[4]), SSL_FILETYPE_PEM) != 1) {
1236 goto out;
1239 if (SSL_accept(ssl) != 1) {
1240 goto out;
1243 else {
1244 if (SSL_connect(ssl) != 1) {
1245 goto out;
1249 af = JimMakeChannel(interp, NULL, fd, NULL, "aio.sslstream%ld", af->addr_family, "r+");
1250 if (af == NULL) {
1251 goto out;
1254 af->ssl = ssl;
1255 af->fops = &ssl_fops;
1257 return JIM_OK;
1259 out:
1260 #if defined(HAVE_DUP)
1261 close(fd);
1262 #endif
1263 SSL_free(ssl);
1264 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1265 return JIM_ERR;
1268 static int aio_cmd_verify(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1270 AioFile *af = Jim_CmdPrivData(interp);
1271 int ret;
1273 if (!af->fops->verify) {
1274 return JIM_OK;
1277 ret = af->fops->verify(af);
1278 if (ret != JIM_OK) {
1279 if (JimCheckStreamError(interp, af)) {
1280 JimAioSetError(interp, af->filename);
1281 } else {
1282 Jim_SetResultString(interp, "failed to verify the connection authenticity", -1);
1285 return ret;
1287 #endif /* JIM_BOOTSTRAP */
1289 #if defined(HAVE_STRUCT_FLOCK) && !defined(JIM_BOOTSTRAP)
1290 static int aio_cmd_lock(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1292 AioFile *af = Jim_CmdPrivData(interp);
1293 struct flock fl;
1295 fl.l_start = 0;
1296 fl.l_len = 0;
1297 fl.l_type = F_WRLCK;
1298 fl.l_whence = SEEK_SET;
1300 switch (fcntl(af->fd, F_SETLK, &fl))
1302 case 0:
1303 Jim_SetResultInt(interp, 1);
1304 break;
1305 case -1:
1306 if (errno == EACCES || errno == EAGAIN)
1307 Jim_SetResultInt(interp, 0);
1308 else
1310 Jim_SetResultFormatted(interp, "lock failed: %s",
1311 strerror(errno));
1312 return JIM_ERR;
1314 break;
1315 default:
1316 Jim_SetResultInt(interp, 0);
1317 break;
1320 return JIM_OK;
1323 static int aio_cmd_unlock(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1325 AioFile *af = Jim_CmdPrivData(interp);
1326 struct flock fl;
1327 fl.l_start = 0;
1328 fl.l_len = 0;
1329 fl.l_type = F_UNLCK;
1330 fl.l_whence = SEEK_SET;
1332 Jim_SetResultInt(interp, fcntl(af->fd, F_SETLK, &fl) == 0);
1333 return JIM_OK;
1335 #endif /* JIM_BOOTSTRAP */
1337 #if defined(HAVE_TERMIOS_H) && !defined(JIM_BOOTSTRAP)
1338 static int aio_cmd_tty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1340 AioFile *af = Jim_CmdPrivData(interp);
1341 Jim_Obj *dictObjPtr;
1342 int ret;
1344 if (argc == 0) {
1345 /* get the current settings as a dictionary */
1346 dictObjPtr = Jim_GetTtySettings(interp, af->fd);
1347 if (dictObjPtr == NULL) {
1348 JimAioSetError(interp, NULL);
1349 return JIM_ERR;
1351 Jim_SetResult(interp, dictObjPtr);
1352 return JIM_OK;
1355 if (argc > 1) {
1356 /* Convert name value arguments to a dictionary */
1357 dictObjPtr = Jim_NewListObj(interp, argv, argc);
1359 else {
1360 /* The settings are already given as a list */
1361 dictObjPtr = argv[0];
1363 Jim_IncrRefCount(dictObjPtr);
1365 if (Jim_ListLength(interp, dictObjPtr) % 2) {
1366 /* Must be a valid dictionary */
1367 Jim_DecrRefCount(interp, dictObjPtr);
1368 return -1;
1371 ret = Jim_SetTtySettings(interp, af->fd, dictObjPtr);
1372 if (ret < 0) {
1373 JimAioSetError(interp, NULL);
1374 ret = JIM_ERR;
1376 Jim_DecrRefCount(interp, dictObjPtr);
1378 return ret;
1380 #endif /* JIM_BOOTSTRAP */
1382 static const jim_subcmd_type aio_command_table[] = {
1383 { "read",
1384 "?-nonewline? ?len?",
1385 aio_cmd_read,
1388 /* Description: Read and return bytes from the stream. To eof if no len. */
1390 { "copyto",
1391 "handle ?size?",
1392 aio_cmd_copy,
1395 /* Description: Copy up to 'size' bytes to the given filehandle, or to eof if no size. */
1397 { "getfd",
1398 NULL,
1399 aio_cmd_getfd,
1402 /* Description: Internal command to return the underlying file descriptor. */
1404 { "gets",
1405 "?var?",
1406 aio_cmd_gets,
1409 /* Description: Read one line and return it or store it in the var */
1411 { "puts",
1412 "?-nonewline? str",
1413 aio_cmd_puts,
1416 /* Description: Write the string, with newline unless -nonewline */
1418 { "isatty",
1419 NULL,
1420 aio_cmd_isatty,
1423 /* Description: Is the file descriptor a tty? */
1425 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
1426 { "recvfrom",
1427 "len ?addrvar?",
1428 aio_cmd_recvfrom,
1431 /* Description: Receive up to 'len' bytes on the socket. Sets 'addrvar' with receive address, if set */
1433 { "sendto",
1434 "str address",
1435 aio_cmd_sendto,
1438 /* Description: Send 'str' to the given address (dgram only) */
1440 { "accept",
1441 "?addrvar?",
1442 aio_cmd_accept,
1445 /* Description: Server socket only: Accept a connection and return stream */
1447 { "listen",
1448 "backlog",
1449 aio_cmd_listen,
1452 /* Description: Set the listen backlog for server socket */
1454 { "sockopt",
1455 "?opt 0|1?",
1456 aio_cmd_sockopt,
1459 /* Description: Return a dictionary of sockopts, or set the value of a sockopt */
1461 #endif /* JIM_BOOTSTRAP */
1462 { "flush",
1463 NULL,
1464 aio_cmd_flush,
1467 /* Description: Flush the stream */
1469 { "eof",
1470 NULL,
1471 aio_cmd_eof,
1474 /* Description: Returns 1 if stream is at eof */
1476 { "close",
1477 "?r(ead)|w(rite)?",
1478 aio_cmd_close,
1481 JIM_MODFLAG_FULLARGV,
1482 /* Description: Closes the stream. */
1484 { "seek",
1485 "offset ?start|current|end",
1486 aio_cmd_seek,
1489 /* Description: Seeks in the stream (default 'current') */
1491 { "tell",
1492 NULL,
1493 aio_cmd_tell,
1496 /* Description: Returns the current seek position */
1498 { "filename",
1499 NULL,
1500 aio_cmd_filename,
1503 /* Description: Returns the original filename */
1505 #ifdef O_NDELAY
1506 { "ndelay",
1507 "?0|1?",
1508 aio_cmd_ndelay,
1511 /* Description: Set O_NDELAY (if arg). Returns current/new setting. */
1513 #endif
1514 #ifdef HAVE_FSYNC
1515 { "sync",
1516 NULL,
1517 aio_cmd_sync,
1520 /* Description: Flush and fsync() the stream */
1522 #endif
1523 { "buffering",
1524 "none|line|full",
1525 aio_cmd_buffering,
1528 /* Description: Sets buffering */
1530 #ifdef jim_ext_eventloop
1531 { "readable",
1532 "?readable-script?",
1533 aio_cmd_readable,
1536 /* Description: Returns script, or invoke readable-script when readable, {} to remove */
1538 { "writable",
1539 "?writable-script?",
1540 aio_cmd_writable,
1543 /* Description: Returns script, or invoke writable-script when writable, {} to remove */
1545 { "onexception",
1546 "?exception-script?",
1547 aio_cmd_onexception,
1550 /* Description: Returns script, or invoke exception-script when oob data, {} to remove */
1552 #endif
1553 #if !defined(JIM_BOOTSTRAP)
1554 #if defined(JIM_SSL)
1555 { "ssl",
1556 "?-server cert priv?",
1557 aio_cmd_ssl,
1560 JIM_MODFLAG_FULLARGV
1561 /* Description: Wraps a stream socket with SSL/TLS and returns a new channel */
1563 { "verify",
1564 NULL,
1565 aio_cmd_verify,
1568 /* Description: Verifies the certificate of a SSL/TLS channel */
1570 #endif
1571 #if defined(HAVE_STRUCT_FLOCK)
1572 { "lock",
1573 NULL,
1574 aio_cmd_lock,
1577 /* Description: Attempt to get a lock. */
1579 { "unlock",
1580 NULL,
1581 aio_cmd_unlock,
1584 /* Description: Relase a lock. */
1586 #endif
1587 #if defined(HAVE_TERMIOS_H)
1588 { "tty",
1589 "?baud rate? ?data bits? ?stop bits? ?parity even|odd|none? ?handshake xonxoff|rtscts|none? ?input raw|cooked? ?output raw|cooked? ?vmin n? ?vtime n?",
1590 aio_cmd_tty,
1593 /* Description: Get or set tty settings - valid only on a tty */
1595 #endif
1596 #endif /* JIM_BOOTSTRAP */
1597 { NULL }
1600 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1602 return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, aio_command_table, argc, argv), argc, argv);
1605 static int JimAioOpenCommand(Jim_Interp *interp, int argc,
1606 Jim_Obj *const *argv)
1608 const char *mode;
1610 if (argc != 2 && argc != 3) {
1611 Jim_WrongNumArgs(interp, 1, argv, "filename ?mode?");
1612 return JIM_ERR;
1615 mode = (argc == 3) ? Jim_String(argv[2]) : "r";
1617 #ifdef jim_ext_tclcompat
1619 const char *filename = Jim_String(argv[1]);
1621 /* If the filename starts with '|', use popen instead */
1622 if (*filename == '|') {
1623 Jim_Obj *evalObj[3];
1625 evalObj[0] = Jim_NewStringObj(interp, "::popen", -1);
1626 evalObj[1] = Jim_NewStringObj(interp, filename + 1, -1);
1627 evalObj[2] = Jim_NewStringObj(interp, mode, -1);
1629 return Jim_EvalObjVector(interp, 3, evalObj);
1632 #endif
1633 return JimMakeChannel(interp, NULL, -1, argv[1], "aio.handle%ld", 0, mode) ? JIM_OK : JIM_ERR;
1636 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1637 static void JimAioSslContextDelProc(struct Jim_Interp *interp, void *privData)
1639 SSL_CTX_free((SSL_CTX *)privData);
1640 ERR_free_strings();
1643 static SSL_CTX *JimAioSslCtx(Jim_Interp *interp)
1645 SSL_CTX *ssl_ctx = (SSL_CTX *)Jim_GetAssocData(interp, "ssl_ctx");
1646 if (ssl_ctx == NULL) {
1647 SSL_load_error_strings();
1648 SSL_library_init();
1649 ssl_ctx = SSL_CTX_new(TLSv1_2_method());
1650 if (ssl_ctx && SSL_CTX_set_default_verify_paths(ssl_ctx)) {
1651 SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
1652 Jim_SetAssocData(interp, "ssl_ctx", JimAioSslContextDelProc, ssl_ctx);
1653 } else {
1654 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1657 return ssl_ctx;
1659 #endif /* JIM_BOOTSTRAP */
1662 * Creates a channel for fh/fd/filename.
1664 * If fh is not NULL, uses that as the channel (and sets AIO_KEEPOPEN).
1665 * Otherwise, if fd is >= 0, uses that as the channel.
1666 * Otherwise opens 'filename' with mode 'mode'.
1668 * hdlfmt is a sprintf format for the filehandle. Anything with %ld at the end will do.
1669 * mode is used for open or fdopen.
1671 * Creates the command and sets the name as the current result.
1672 * Returns the AioFile pointer on sucess or NULL on failure.
1674 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
1675 const char *hdlfmt, int family, const char *mode)
1677 AioFile *af;
1678 char buf[AIO_CMD_LEN];
1679 int openFlags = 0;
1681 snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
1683 if (fh) {
1684 openFlags = AIO_KEEPOPEN;
1687 snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
1688 if (!filename) {
1689 filename = Jim_NewStringObj(interp, buf, -1);
1692 Jim_IncrRefCount(filename);
1694 if (fh == NULL) {
1695 if (fd >= 0) {
1696 #ifndef JIM_ANSIC
1697 fh = fdopen(fd, mode);
1698 #endif
1700 else
1701 fh = fopen(Jim_String(filename), mode);
1703 if (fh == NULL) {
1704 JimAioSetError(interp, filename);
1705 #ifndef JIM_ANSIC
1706 if (fd >= 0) {
1707 close(fd);
1709 #endif
1710 Jim_DecrRefCount(interp, filename);
1711 return NULL;
1715 /* Create the file command */
1716 af = Jim_Alloc(sizeof(*af));
1717 memset(af, 0, sizeof(*af));
1718 af->fp = fh;
1719 #ifndef JIM_ANSIC
1720 af->fd = fileno(fh);
1721 #endif
1722 af->filename = filename;
1723 #ifdef FD_CLOEXEC
1724 if ((openFlags & AIO_KEEPOPEN) == 0) {
1725 (void)fcntl(af->fd, F_SETFD, FD_CLOEXEC);
1727 #endif
1728 af->openFlags = openFlags;
1729 af->addr_family = family;
1730 af->fops = &stdio_fops;
1731 af->ssl = NULL;
1733 Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc);
1735 /* Note that the command must use the global namespace, even if
1736 * the current namespace is something different
1738 Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, Jim_NewStringObj(interp, buf, -1)));
1740 return af;
1743 #if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && defined(HAVE_SYS_UN_H))
1745 * Create a pair of channels. e.g. from pipe() or socketpair()
1747 static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename,
1748 const char *hdlfmt, int family, const char *mode[2])
1750 if (JimMakeChannel(interp, NULL, p[0], filename, hdlfmt, family, mode[0])) {
1751 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
1752 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
1753 if (JimMakeChannel(interp, NULL, p[1], filename, hdlfmt, family, mode[1])) {
1754 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
1755 Jim_SetResult(interp, objPtr);
1756 return JIM_OK;
1760 /* Can only be here if fdopen() failed */
1761 close(p[0]);
1762 close(p[1]);
1763 JimAioSetError(interp, NULL);
1764 return JIM_ERR;
1766 #endif
1768 #ifdef HAVE_PIPE
1769 static int JimAioPipeCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1771 int p[2];
1772 static const char *mode[2] = { "r", "w" };
1774 if (argc != 1) {
1775 Jim_WrongNumArgs(interp, 1, argv, "");
1776 return JIM_ERR;
1779 if (pipe(p) != 0) {
1780 JimAioSetError(interp, NULL);
1781 return JIM_ERR;
1784 return JimMakeChannelPair(interp, p, argv[0], "aio.pipe%ld", 0, mode);
1786 #endif
1788 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
1790 static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1792 const char *hdlfmt = "aio.unknown%ld";
1793 const char *socktypes[] = {
1794 "unix",
1795 "unix.server",
1796 "dgram",
1797 "dgram.server",
1798 "stream",
1799 "stream.server",
1800 "pipe",
1801 "pair",
1802 NULL
1804 enum
1806 SOCK_UNIX,
1807 SOCK_UNIX_SERVER,
1808 SOCK_DGRAM_CLIENT,
1809 SOCK_DGRAM_SERVER,
1810 SOCK_STREAM_CLIENT,
1811 SOCK_STREAM_SERVER,
1812 SOCK_STREAM_PIPE,
1813 SOCK_STREAM_SOCKETPAIR,
1815 int socktype;
1816 int sock;
1817 const char *hostportarg = NULL;
1818 int res;
1819 int on = 1;
1820 const char *mode = "r+";
1821 int family = PF_INET;
1822 Jim_Obj *argv0 = argv[0];
1823 int ipv6 = 0;
1825 if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-ipv6")) {
1826 if (!IPV6) {
1827 Jim_SetResultString(interp, "ipv6 not supported", -1);
1828 return JIM_ERR;
1830 ipv6 = 1;
1831 family = PF_INET6;
1833 argc -= ipv6;
1834 argv += ipv6;
1836 if (argc < 2) {
1837 wrongargs:
1838 Jim_WrongNumArgs(interp, 1, &argv0, "?-ipv6? type ?address?");
1839 return JIM_ERR;
1842 if (Jim_GetEnum(interp, argv[1], socktypes, &socktype, "socket type", JIM_ERRMSG) != JIM_OK)
1843 return Jim_CheckShowCommands(interp, argv[1], socktypes);
1845 Jim_SetEmptyResult(interp);
1847 hdlfmt = "aio.sock%ld";
1849 if (argc > 2) {
1850 hostportarg = Jim_String(argv[2]);
1853 switch (socktype) {
1854 case SOCK_DGRAM_CLIENT:
1855 if (argc == 2) {
1856 /* No address, so an unconnected dgram socket */
1857 sock = socket(family, SOCK_DGRAM, 0);
1858 if (sock < 0) {
1859 JimAioSetError(interp, NULL);
1860 return JIM_ERR;
1862 break;
1864 /* fall through */
1865 case SOCK_STREAM_CLIENT:
1867 union sockaddr_any sa;
1868 int salen;
1870 if (argc != 3) {
1871 goto wrongargs;
1874 if (ipv6) {
1875 if (JimParseIPv6Address(interp, hostportarg, &sa, &salen) != JIM_OK) {
1876 return JIM_ERR;
1879 else if (JimParseIpAddress(interp, hostportarg, &sa, &salen) != JIM_OK) {
1880 return JIM_ERR;
1882 sock = socket(family, (socktype == SOCK_DGRAM_CLIENT) ? SOCK_DGRAM : SOCK_STREAM, 0);
1883 if (sock < 0) {
1884 JimAioSetError(interp, NULL);
1885 return JIM_ERR;
1887 res = connect(sock, &sa.sa, salen);
1888 if (res) {
1889 JimAioSetError(interp, argv[2]);
1890 close(sock);
1891 return JIM_ERR;
1894 break;
1896 case SOCK_STREAM_SERVER:
1897 case SOCK_DGRAM_SERVER:
1899 union sockaddr_any sa;
1900 int salen;
1902 if (argc != 3) {
1903 goto wrongargs;
1906 if (ipv6) {
1907 if (JimParseIPv6Address(interp, hostportarg, &sa, &salen) != JIM_OK) {
1908 return JIM_ERR;
1911 else if (JimParseIpAddress(interp, hostportarg, &sa, &salen) != JIM_OK) {
1912 return JIM_ERR;
1914 sock = socket(family, (socktype == SOCK_DGRAM_SERVER) ? SOCK_DGRAM : SOCK_STREAM, 0);
1915 if (sock < 0) {
1916 JimAioSetError(interp, NULL);
1917 return JIM_ERR;
1920 /* Enable address reuse */
1921 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
1923 res = bind(sock, &sa.sa, salen);
1924 if (res) {
1925 JimAioSetError(interp, argv[2]);
1926 close(sock);
1927 return JIM_ERR;
1929 if (socktype == SOCK_STREAM_SERVER) {
1930 res = listen(sock, 5);
1931 if (res) {
1932 JimAioSetError(interp, NULL);
1933 close(sock);
1934 return JIM_ERR;
1937 hdlfmt = "aio.socksrv%ld";
1939 break;
1941 #ifdef HAVE_SYS_UN_H
1942 case SOCK_UNIX:
1944 struct sockaddr_un sa;
1945 socklen_t len;
1947 if (argc != 3 || ipv6) {
1948 goto wrongargs;
1951 if (JimParseDomainAddress(interp, hostportarg, &sa) != JIM_OK) {
1952 JimAioSetError(interp, argv[2]);
1953 return JIM_ERR;
1955 family = PF_UNIX;
1956 sock = socket(PF_UNIX, SOCK_STREAM, 0);
1957 if (sock < 0) {
1958 JimAioSetError(interp, NULL);
1959 return JIM_ERR;
1961 len = strlen(sa.sun_path) + 1 + sizeof(sa.sun_family);
1962 res = connect(sock, (struct sockaddr *)&sa, len);
1963 if (res) {
1964 JimAioSetError(interp, argv[2]);
1965 close(sock);
1966 return JIM_ERR;
1968 hdlfmt = "aio.sockunix%ld";
1969 break;
1972 case SOCK_UNIX_SERVER:
1974 struct sockaddr_un sa;
1975 socklen_t len;
1977 if (argc != 3 || ipv6) {
1978 goto wrongargs;
1981 if (JimParseDomainAddress(interp, hostportarg, &sa) != JIM_OK) {
1982 JimAioSetError(interp, argv[2]);
1983 return JIM_ERR;
1985 family = PF_UNIX;
1986 sock = socket(PF_UNIX, SOCK_STREAM, 0);
1987 if (sock < 0) {
1988 JimAioSetError(interp, NULL);
1989 return JIM_ERR;
1991 len = strlen(sa.sun_path) + 1 + sizeof(sa.sun_family);
1992 res = bind(sock, (struct sockaddr *)&sa, len);
1993 if (res) {
1994 JimAioSetError(interp, argv[2]);
1995 close(sock);
1996 return JIM_ERR;
1998 res = listen(sock, 5);
1999 if (res) {
2000 JimAioSetError(interp, NULL);
2001 close(sock);
2002 return JIM_ERR;
2004 hdlfmt = "aio.sockunixsrv%ld";
2005 break;
2007 #endif
2009 #if defined(HAVE_SOCKETPAIR) && defined(HAVE_SYS_UN_H)
2010 case SOCK_STREAM_SOCKETPAIR:
2012 int p[2];
2013 static const char *mode[2] = { "r+", "r+" };
2015 if (argc != 2 || ipv6) {
2016 goto wrongargs;
2019 if (socketpair(PF_UNIX, SOCK_STREAM, 0, p) < 0) {
2020 JimAioSetError(interp, NULL);
2021 return JIM_ERR;
2023 return JimMakeChannelPair(interp, p, argv[1], "aio.sockpair%ld", PF_UNIX, mode);
2025 break;
2026 #endif
2028 #if defined(HAVE_PIPE)
2029 case SOCK_STREAM_PIPE:
2030 if (argc != 2 || ipv6) {
2031 goto wrongargs;
2033 return JimAioPipeCommand(interp, 1, &argv[1]);
2034 #endif
2036 default:
2037 Jim_SetResultString(interp, "Unsupported socket type", -1);
2038 return JIM_ERR;
2041 return JimMakeChannel(interp, NULL, sock, argv[1], hdlfmt, family, mode) ? JIM_OK : JIM_ERR;
2043 #endif /* JIM_BOOTSTRAP */
2045 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
2046 static int JimAioLoadSSLCertsCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2048 SSL_CTX *ssl_ctx;
2050 if (argc != 2) {
2051 Jim_WrongNumArgs(interp, 1, argv, "dir");
2052 return JIM_ERR;
2055 ssl_ctx = JimAioSslCtx(interp);
2056 if (!ssl_ctx) {
2057 return JIM_ERR;
2059 if (SSL_CTX_load_verify_locations(ssl_ctx, NULL, Jim_String(argv[1])) == 1) {
2060 return JIM_OK;
2062 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
2063 return JIM_ERR;
2065 #endif /* JIM_BOOTSTRAP */
2067 int Jim_aioInit(Jim_Interp *interp)
2069 if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG))
2070 return JIM_ERR;
2072 #if defined(JIM_SSL)
2073 Jim_CreateCommand(interp, "load_ssl_certs", JimAioLoadSSLCertsCommand, NULL, NULL);
2074 #endif
2076 Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL);
2077 #ifdef HAVE_SOCKETS
2078 Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL);
2079 #endif
2080 #ifdef HAVE_PIPE
2081 Jim_CreateCommand(interp, "pipe", JimAioPipeCommand, NULL, NULL);
2082 #endif
2084 /* Create filehandles for stdin, stdout and stderr */
2085 JimMakeChannel(interp, stdin, -1, NULL, "stdin", 0, "r");
2086 JimMakeChannel(interp, stdout, -1, NULL, "stdout", 0, "w");
2087 JimMakeChannel(interp, stderr, -1, NULL, "stderr", 0, "w");
2089 return JIM_OK;