aio: ssl: connection close isn't an error
[jimtcl.git] / jim-aio.c
blobe189781c3e4291f4d25430ef7aab82d0f7ce2047
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 #include <assert.h>
50 #ifdef HAVE_UNISTD_H
51 #include <unistd.h>
52 #include <sys/stat.h>
53 #endif
54 #ifdef HAVE_UTIL_H
55 #include <util.h>
56 #endif
57 #ifdef HAVE_PTY_H
58 #include <pty.h>
59 #endif
61 #include "jim.h"
62 #include "jimiocompat.h"
64 #if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H)
65 #include <sys/socket.h>
66 #include <netinet/in.h>
67 #include <netinet/tcp.h>
68 #include <arpa/inet.h>
69 #include <netdb.h>
70 #ifdef HAVE_SYS_UN_H
71 #include <sys/un.h>
72 #endif
73 #define HAVE_SOCKETS
74 #elif defined (__MINGW32__)
75 /* currently mingw32 doesn't support sockets, but has pipe, fdopen */
76 #else
77 #define JIM_ANSIC
78 #endif
80 #if defined(JIM_SSL)
81 #include <openssl/ssl.h>
82 #include <openssl/err.h>
83 #endif
85 #ifdef HAVE_TERMIOS_H
86 #include <jim-tty.h>
87 #endif
89 #include "jim-eventloop.h"
90 #include "jim-subcmd.h"
92 #define AIO_CMD_LEN 32 /* e.g. aio.handleXXXXXX */
93 #define AIO_BUF_LEN 256 /* Can keep this small and rely on stdio buffering */
95 #ifndef HAVE_FTELLO
96 #define ftello ftell
97 #endif
98 #ifndef HAVE_FSEEKO
99 #define fseeko fseek
100 #endif
102 #define AIO_KEEPOPEN 1
103 #define AIO_NODELETE 2
104 #define AIO_EOF 4
106 #if defined(JIM_IPV6)
107 #define IPV6 1
108 #else
109 #define IPV6 0
110 #ifndef PF_INET6
111 #define PF_INET6 0
112 #endif
113 #endif
114 #if defined(HAVE_SYS_UN_H) && defined(PF_UNIX)
115 #define UNIX_SOCKETS 1
116 #else
117 #define UNIX_SOCKETS 0
118 #endif
120 #ifdef JIM_ANSIC
121 /* no fdopen() with ANSIC, so can't support these */
122 #undef HAVE_PIPE
123 #undef HAVE_SOCKETPAIR
124 #endif
126 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
127 /* Avoid type punned pointers */
128 union sockaddr_any {
129 struct sockaddr sa;
130 struct sockaddr_in sin;
131 #if IPV6
132 struct sockaddr_in6 sin6;
133 #endif
134 #if UNIX_SOCKETS
135 struct sockaddr_un sun;
136 #endif
139 #ifndef HAVE_INET_NTOP
140 const char *inet_ntop(int af, const void *src, char *dst, int size)
142 if (af != PF_INET) {
143 return NULL;
145 snprintf(dst, size, "%s", inet_ntoa(((struct sockaddr_in *)src)->sin_addr));
146 return dst;
148 #endif
149 #endif /* JIM_BOOTSTRAP */
151 struct AioFile;
153 typedef struct {
154 int (*writer)(struct AioFile *af, const char *buf, int len);
155 int (*reader)(struct AioFile *af, char *buf, int len);
156 const char *(*getline)(struct AioFile *af, char *buf, int len);
157 int (*error)(const struct AioFile *af);
158 const char *(*strerror)(struct AioFile *af);
159 int (*verify)(struct AioFile *af);
160 int (*eof)(struct AioFile *af);
161 int (*pending)(struct AioFile *af);
162 } JimAioFopsType;
164 typedef struct AioFile
166 FILE *fp;
167 Jim_Obj *filename;
168 int type;
169 int flags; /* AIO_KEEPOPEN? keep FILE* */
170 int fd;
171 Jim_Obj *rEvent;
172 Jim_Obj *wEvent;
173 Jim_Obj *eEvent;
174 int addr_family;
175 void *ssl;
176 const JimAioFopsType *fops;
177 } AioFile;
179 static int stdio_writer(struct AioFile *af, const char *buf, int len)
181 return fwrite(buf, 1, len, af->fp);
184 static int stdio_reader(struct AioFile *af, char *buf, int len)
186 return fread(buf, 1, len, af->fp);
189 static const char *stdio_getline(struct AioFile *af, char *buf, int len)
191 return fgets(buf, len, af->fp);
194 static int stdio_error(const AioFile *af)
196 if (!ferror(af->fp)) {
197 return JIM_OK;
199 clearerr(af->fp);
200 /* EAGAIN and similar are not error conditions. Just treat them like eof */
201 if (feof(af->fp) || errno == EAGAIN || errno == EINTR) {
202 return JIM_OK;
204 #ifdef ECONNRESET
205 if (errno == ECONNRESET) {
206 return JIM_OK;
208 #endif
209 #ifdef ECONNABORTED
210 if (errno == ECONNABORTED) {
211 return JIM_OK;
213 #endif
214 return JIM_ERR;
217 static const char *stdio_strerror(struct AioFile *af)
219 return strerror(errno);
222 static int stdio_eof(struct AioFile *af)
224 return feof(af->fp);
227 static const JimAioFopsType stdio_fops = {
228 stdio_writer,
229 stdio_reader,
230 stdio_getline,
231 stdio_error,
232 stdio_strerror,
233 NULL, /* verify */
234 stdio_eof,
235 NULL, /* pending */
238 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
240 static SSL_CTX *JimAioSslCtx(Jim_Interp *interp);
242 static int ssl_writer(struct AioFile *af, const char *buf, int len)
244 return SSL_write(af->ssl, buf, len);
247 static int ssl_pending(struct AioFile *af)
249 return SSL_pending(af->ssl);
252 static int ssl_reader(struct AioFile *af, char *buf, int len)
254 int ret = SSL_read(af->ssl, buf, len);
255 switch (SSL_get_error(af->ssl, ret)) {
256 case SSL_ERROR_NONE:
257 return ret;
258 case SSL_ERROR_SYSCALL:
259 case SSL_ERROR_ZERO_RETURN:
260 if (errno != EAGAIN) {
261 af->flags |= AIO_EOF;
263 return 0;
264 case SSL_ERROR_SSL:
265 default:
266 if (errno == EAGAIN) {
267 return 0;
269 af->flags |= AIO_EOF;
270 return -1;
274 static int ssl_eof(struct AioFile *af)
276 return (af->flags & AIO_EOF);
279 static const char *ssl_getline(struct AioFile *af, char *buf, int len)
281 size_t i;
282 for (i = 0; i < len - 1 && !ssl_eof(af); i++) {
283 int ret = ssl_reader(af, &buf[i], 1);
284 if (ret != 1) {
285 break;
287 if (buf[i] == '\n') {
288 i++;
289 break;
292 buf[i] = '\0';
293 if (i == 0 && ssl_eof(af)) {
294 return NULL;
296 return buf;
299 static int ssl_error(const struct AioFile *af)
301 int ret = SSL_get_error(af->ssl, 0);
302 /* XXX should we be following the same logic as ssl_reader() here? */
303 if (ret == SSL_ERROR_ZERO_RETURN || ret == SSL_ERROR_NONE) {
304 return JIM_OK;
306 if (ret == SSL_ERROR_SYSCALL) {
307 return stdio_error(af);
309 return JIM_ERR;
312 static const char *ssl_strerror(struct AioFile *af)
314 int err = ERR_get_error();
316 if (err) {
317 return ERR_error_string(err, NULL);
319 else {
320 return stdio_strerror(af);
324 static int ssl_verify(struct AioFile *af)
326 X509 *cert;
328 cert = SSL_get_peer_certificate(af->ssl);
329 if (!cert) {
330 return JIM_ERR;
332 X509_free(cert);
334 if (SSL_get_verify_result(af->ssl) == X509_V_OK) {
335 return JIM_OK;
338 return JIM_ERR;
341 static const JimAioFopsType ssl_fops = {
342 ssl_writer,
343 ssl_reader,
344 ssl_getline,
345 ssl_error,
346 ssl_strerror,
347 ssl_verify,
348 ssl_eof,
349 ssl_pending,
351 #endif /* JIM_BOOTSTRAP */
353 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
354 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
355 const char *hdlfmt, int family, const char *mode, int flags);
357 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
358 #ifndef HAVE_GETADDRINFO
360 * Poor man's getaddrinfo().
361 * hints->ai_family must be set and must be PF_INET or PF_INET6
362 * Only returns the first matching result.
363 * servname must be numeric.
365 struct addrinfo {
366 int ai_family;
367 socklen_t ai_addrlen;
368 struct sockaddr *ai_addr; /* simply points to ai_storage */
369 union sockaddr_any ai_storage;
372 static int getaddrinfo(const char *hostname, const char *servname,
373 const struct addrinfo *hints, struct addrinfo **res)
375 struct hostent *he;
376 char *end;
377 unsigned long port = strtoul(servname, &end, 10);
378 if (port == 0 || port > 65536 || *end) {
379 errno = EINVAL;
380 return -1;
383 if ((he = gethostbyname(hostname)) != NULL) {
384 int i;
385 for (i = 0; he->h_addr_list[i]; i++) {
386 if (he->h_addrtype == hints->ai_family) {
387 struct addrinfo *ai = malloc(sizeof(*ai));
388 memset(ai, 0, sizeof(*ai));
389 ai->ai_family = he->h_addrtype;
390 ai->ai_addr = &ai->ai_storage.sa;
391 if (ai->ai_family == PF_INET) {
392 ai->ai_addrlen = sizeof(ai->ai_storage.sin);
393 ai->ai_storage.sin.sin_family = he->h_addrtype;
394 assert(sizeof(ai->ai_storage.sin.sin_addr) == he->h_length);
395 memcpy(&ai->ai_storage.sin.sin_addr, he->h_addr_list[i], he->h_length);
396 ai->ai_storage.sin.sin_port = htons(port);
398 #if IPV6
399 else {
400 ai->ai_addrlen = sizeof(ai->ai_storage.sin6);
401 ai->ai_storage.sin6.sin6_family = he->h_addrtype;
402 assert(sizeof(ai->ai_storage.sin6.sin6_addr) == he->h_length);
403 memcpy(&ai->ai_storage.sin6.sin6_addr, he->h_addr_list[i], he->h_length);
404 ai->ai_storage.sin6.sin6_port = htons(port);
406 #endif
407 *res = ai;
408 return 0;
412 return ENOENT;
415 static void freeaddrinfo(struct addrinfo *ai)
417 free(ai);
419 #endif
421 static int JimParseIPv6Address(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, socklen_t *salen)
423 #if IPV6
425 * An IPv6 addr/port looks like:
426 * [::1]
427 * [::1]:2000
428 * [fe80::223:6cff:fe95:bdc0%en1]:2000
429 * [::]:2000
430 * 2000
432 * Note that the "any" address is ::, which is the same as when no address is specified.
434 char *sthost = NULL;
435 const char *stport;
436 int ret = JIM_OK;
437 struct addrinfo req;
438 struct addrinfo *ai;
440 stport = strrchr(hostport, ':');
441 if (!stport) {
442 /* No : so, the whole thing is the port */
443 stport = hostport;
444 hostport = "::";
445 sthost = Jim_StrDup(hostport);
447 else {
448 stport++;
451 if (*hostport == '[') {
452 /* This is a numeric ipv6 address */
453 char *pt = strchr(++hostport, ']');
454 if (pt) {
455 sthost = Jim_StrDupLen(hostport, pt - hostport);
459 if (!sthost) {
460 sthost = Jim_StrDupLen(hostport, stport - hostport - 1);
463 memset(&req, '\0', sizeof(req));
464 req.ai_family = PF_INET6;
466 if (getaddrinfo(sthost, stport, &req, &ai)) {
467 Jim_SetResultFormatted(interp, "Not a valid address: %s:%s", sthost, stport);
468 ret = JIM_ERR;
470 else {
471 memcpy(&sa->sin6, ai->ai_addr, ai->ai_addrlen);
472 *salen = ai->ai_addrlen;
473 freeaddrinfo(ai);
475 Jim_Free(sthost);
477 return ret;
478 #else
479 Jim_SetResultString(interp, "ipv6 not supported", -1);
480 return JIM_ERR;
481 #endif
484 static int JimParseIpAddress(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, socklen_t *salen)
486 /* An IPv4 addr/port looks like:
487 * 192.168.1.5
488 * 192.168.1.5:2000
489 * 2000
491 * If the address is missing, INADDR_ANY is used.
492 * If the port is missing, 0 is used (only useful for server sockets).
494 char *sthost = NULL;
495 const char *stport;
496 int ret = JIM_OK;
497 struct addrinfo req;
498 struct addrinfo *ai;
500 stport = strrchr(hostport, ':');
501 if (!stport) {
502 /* No : so, the whole thing is the port */
503 stport = hostport;
504 sthost = Jim_StrDup("0.0.0.0");
506 else {
507 sthost = Jim_StrDupLen(hostport, stport - hostport);
508 stport++;
511 memset(&req, '\0', sizeof(req));
512 req.ai_family = PF_INET;
514 if (getaddrinfo(sthost, stport, &req, &ai)) {
515 ret = JIM_ERR;
517 else {
518 memcpy(&sa->sin, ai->ai_addr, ai->ai_addrlen);
519 *salen = ai->ai_addrlen;
520 freeaddrinfo(ai);
522 Jim_Free(sthost);
524 if (ret != JIM_OK) {
525 Jim_SetResultFormatted(interp, "Not a valid address: %s", hostport);
528 return ret;
531 #if UNIX_SOCKETS
532 static int JimParseDomainAddress(Jim_Interp *interp, const char *path, union sockaddr_any *sa, socklen_t *salen)
534 sa->sun.sun_family = PF_UNIX;
535 snprintf(sa->sun.sun_path, sizeof(sa->sun.sun_path), "%s", path);
536 *salen = strlen(sa->sun.sun_path) + 1 + sizeof(sa->sun.sun_family);
538 return JIM_OK;
540 #endif
542 static int JimParseSocketAddress(Jim_Interp *interp, int family, const char *addr, union sockaddr_any *sa, socklen_t *salen)
544 switch (family) {
545 #if UNIX_SOCKETS
546 case PF_UNIX:
547 return JimParseDomainAddress(interp, addr, sa, salen);
548 #endif
549 case PF_INET6:
550 return JimParseIPv6Address(interp, addr, sa, salen);
551 case PF_INET:
552 return JimParseIpAddress(interp, addr, sa, salen);
554 return JIM_ERR;
558 * Format that address in 'sa' as a string and return it as a zero-refcount object.
561 static Jim_Obj *JimFormatSocketAddress(Jim_Interp *interp, const union sockaddr_any *sa, socklen_t salen)
563 /* INET6_ADDRSTRLEN is 46. Add some for [] and port */
564 char addrbuf[60];
565 const char *addr = addrbuf;
566 int addrlen = -1;
568 switch (sa->sa.sa_family) {
569 #if UNIX_SOCKETS
570 case PF_UNIX:
571 addr = sa->sun.sun_path;
572 addrlen = salen - 1 - sizeof(sa->sun.sun_family);
573 if (addrlen < 0) {
574 addrlen = 0;
576 break;
577 #endif
578 #if IPV6
579 case PF_INET6:
580 addrbuf[0] = '[';
581 /* Allow 9 for []:65535\0 */
582 inet_ntop(sa->sa.sa_family, &sa->sin6.sin6_addr, addrbuf + 1, sizeof(addrbuf) - 9);
583 snprintf(addrbuf + strlen(addrbuf), 8, "]:%d", ntohs(sa->sin6.sin6_port));
584 break;
585 #endif
586 case PF_INET:
587 /* Allow 7 for :65535\0 */
588 inet_ntop(sa->sa.sa_family, &sa->sin.sin_addr, addrbuf, sizeof(addrbuf) - 7);
589 snprintf(addrbuf + strlen(addrbuf), 7, ":%d", ntohs(sa->sin.sin_port));
590 break;
592 default:
593 /* Otherwise just an empty address */
594 addr = "";
595 break;
598 return Jim_NewStringObj(interp, addr, addrlen);
601 static int JimSetVariableSocketAddress(Jim_Interp *interp, Jim_Obj *varObjPtr, const union sockaddr_any *sa, socklen_t salen)
603 int ret;
604 Jim_Obj *objPtr = JimFormatSocketAddress(interp, sa, salen);
605 Jim_IncrRefCount(objPtr);
606 ret = Jim_SetVariable(interp, varObjPtr, objPtr);
607 Jim_DecrRefCount(interp, objPtr);
608 return ret;
611 static Jim_Obj *aio_sockname(Jim_Interp *interp, AioFile *af)
613 union sockaddr_any sa;
614 socklen_t salen = sizeof(sa);
616 if (getsockname(af->fd, &sa.sa, &salen) < 0) {
617 return NULL;
619 return JimFormatSocketAddress(interp, &sa, salen);
621 #endif /* JIM_BOOTSTRAP */
623 static const char *JimAioErrorString(AioFile *af)
625 if (af && af->fops)
626 return af->fops->strerror(af);
628 return strerror(errno);
631 static void JimAioSetError(Jim_Interp *interp, Jim_Obj *name)
633 AioFile *af = Jim_CmdPrivData(interp);
635 if (name) {
636 Jim_SetResultFormatted(interp, "%#s: %s", name, JimAioErrorString(af));
638 else {
639 Jim_SetResultString(interp, JimAioErrorString(af), -1);
643 static int JimCheckStreamError(Jim_Interp *interp, AioFile *af)
645 int ret = af->fops->error(af);
646 if (ret) {
647 JimAioSetError(interp, af->filename);
649 return ret;
652 static void JimAioDelProc(Jim_Interp *interp, void *privData)
654 AioFile *af = privData;
656 JIM_NOTUSED(interp);
658 #if UNIX_SOCKETS
659 if (af->addr_family == PF_UNIX && (af->flags & AIO_NODELETE) == 0) {
660 /* If this is bound, delete the socket file now */
661 Jim_Obj *filenameObj = aio_sockname(interp, af);
662 if (filenameObj) {
663 if (Jim_Length(filenameObj)) {
664 remove(Jim_String(filenameObj));
666 Jim_FreeNewObj(interp, filenameObj);
669 #endif
671 Jim_DecrRefCount(interp, af->filename);
673 #ifdef jim_ext_eventloop
674 /* remove all existing EventHandlers */
675 Jim_DeleteFileHandler(interp, af->fd, JIM_EVENT_READABLE | JIM_EVENT_WRITABLE | JIM_EVENT_EXCEPTION);
676 #endif
678 #if defined(JIM_SSL)
679 if (af->ssl != NULL) {
680 SSL_free(af->ssl);
682 #endif
683 if (!(af->flags & AIO_KEEPOPEN)) {
684 fclose(af->fp);
687 Jim_Free(af);
690 static int aio_cmd_read(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
692 AioFile *af = Jim_CmdPrivData(interp);
693 char buf[AIO_BUF_LEN];
694 Jim_Obj *objPtr;
695 int nonewline = 0;
696 int pending = 0;
697 jim_wide neededLen = -1; /* -1 is "read as much as possible" */
698 static const char * const options[] = { "-pending", "-nonewline", NULL };
699 enum { OPT_PENDING, OPT_NONEWLINE };
700 int option;
702 if (argc) {
703 if (*Jim_String(argv[0]) == '-') {
704 if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
705 return JIM_ERR;
707 switch (option) {
708 case OPT_PENDING:
709 if (!af->fops->pending) {
710 Jim_SetResultString(interp, "-pending not supported on this connection type", -1);
711 return JIM_ERR;
713 pending++;
714 break;
715 case OPT_NONEWLINE:
716 nonewline++;
717 break;
720 else {
721 if (Jim_GetWide(interp, argv[0], &neededLen) != JIM_OK)
722 return JIM_ERR;
723 if (neededLen < 0) {
724 Jim_SetResultString(interp, "invalid parameter: negative len", -1);
725 return JIM_ERR;
728 argc--;
729 argv++;
731 if (argc) {
732 return -1;
734 objPtr = Jim_NewStringObj(interp, NULL, 0);
735 while (neededLen != 0) {
736 int retval;
737 int readlen;
739 if (neededLen == -1) {
740 readlen = AIO_BUF_LEN;
742 else {
743 readlen = (neededLen > AIO_BUF_LEN ? AIO_BUF_LEN : neededLen);
745 retval = af->fops->reader(af, buf, pending ? 1 : readlen);
746 if (retval > 0) {
747 Jim_AppendString(interp, objPtr, buf, retval);
748 if (neededLen != -1) {
749 neededLen -= retval;
751 else if (pending) {
752 /* If pending was specified, after we do the initial read,
753 * we do a second read to fetch any buffered data
755 neededLen = af->fops->pending(af);
758 if (retval <= 0) {
759 break;
762 /* Check for error conditions */
763 if (JimCheckStreamError(interp, af)) {
764 Jim_FreeNewObj(interp, objPtr);
765 return JIM_ERR;
767 if (nonewline) {
768 int len;
769 const char *s = Jim_GetString(objPtr, &len);
771 if (len > 0 && s[len - 1] == '\n') {
772 objPtr->length--;
773 objPtr->bytes[objPtr->length] = '\0';
776 Jim_SetResult(interp, objPtr);
777 return JIM_OK;
780 AioFile *Jim_AioFile(Jim_Interp *interp, Jim_Obj *command)
782 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, command, JIM_ERRMSG);
784 /* XXX: There ought to be a supported API for this */
785 if (cmdPtr && !cmdPtr->isproc && cmdPtr->u.native.cmdProc == JimAioSubCmdProc) {
786 return (AioFile *) cmdPtr->u.native.privData;
788 Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", command);
789 return NULL;
792 FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command)
794 AioFile *af;
796 af = Jim_AioFile(interp, command);
797 if (af == NULL) {
798 return NULL;
801 return af->fp;
804 static int aio_cmd_getfd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
806 AioFile *af = Jim_CmdPrivData(interp);
808 fflush(af->fp);
809 Jim_SetResultInt(interp, fileno(af->fp));
811 return JIM_OK;
814 static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
816 AioFile *af = Jim_CmdPrivData(interp);
817 jim_wide count = 0;
818 jim_wide maxlen = JIM_WIDE_MAX;
819 AioFile *outf = Jim_AioFile(interp, argv[0]);
821 if (outf == NULL) {
822 return JIM_ERR;
825 if (argc == 2) {
826 if (Jim_GetWide(interp, argv[1], &maxlen) != JIM_OK) {
827 return JIM_ERR;
831 while (count < maxlen) {
832 /* A reasonable compromise between stack size and speed */
833 char buf[AIO_BUF_LEN];
834 jim_wide len = maxlen - count;
835 if (len > sizeof(buf)) {
836 len = sizeof(buf);
839 len = af->fops->reader(af, buf, len);
840 if (len <= 0) {
841 break;
843 if (outf->fops->writer(outf, buf, len) != len) {
844 break;
846 count += len;
849 if (JimCheckStreamError(interp, af) || JimCheckStreamError(interp, outf)) {
850 return JIM_ERR;
853 Jim_SetResultInt(interp, count);
855 return JIM_OK;
858 static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
860 AioFile *af = Jim_CmdPrivData(interp);
861 char buf[AIO_BUF_LEN];
862 Jim_Obj *objPtr;
863 int len;
865 errno = 0;
867 objPtr = Jim_NewStringObj(interp, NULL, 0);
868 while (1) {
869 buf[AIO_BUF_LEN - 1] = '_';
871 if (af->fops->getline(af, buf, AIO_BUF_LEN) == NULL)
872 break;
874 if (buf[AIO_BUF_LEN - 1] == '\0' && buf[AIO_BUF_LEN - 2] != '\n') {
875 Jim_AppendString(interp, objPtr, buf, AIO_BUF_LEN - 1);
877 else {
878 len = strlen(buf);
880 if (len && (buf[len - 1] == '\n')) {
881 /* strip "\n" */
882 len--;
885 Jim_AppendString(interp, objPtr, buf, len);
886 break;
890 if (JimCheckStreamError(interp, af)) {
891 /* I/O error */
892 Jim_FreeNewObj(interp, objPtr);
893 return JIM_ERR;
896 if (argc) {
897 if (Jim_SetVariable(interp, argv[0], objPtr) != JIM_OK) {
898 Jim_FreeNewObj(interp, objPtr);
899 return JIM_ERR;
902 len = Jim_Length(objPtr);
904 if (len == 0 && af->fops->eof(af)) {
905 /* On EOF returns -1 if varName was specified */
906 len = -1;
908 Jim_SetResultInt(interp, len);
910 else {
911 Jim_SetResult(interp, objPtr);
913 return JIM_OK;
916 static int aio_cmd_puts(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
918 AioFile *af = Jim_CmdPrivData(interp);
919 int wlen;
920 const char *wdata;
921 Jim_Obj *strObj;
923 if (argc == 2) {
924 if (!Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
925 return -1;
927 strObj = argv[1];
929 else {
930 strObj = argv[0];
933 wdata = Jim_GetString(strObj, &wlen);
934 if (af->fops->writer(af, wdata, wlen) == wlen) {
935 if (argc == 2 || af->fops->writer(af, "\n", 1) == 1) {
936 return JIM_OK;
939 JimAioSetError(interp, af->filename);
940 return JIM_ERR;
943 static int aio_cmd_isatty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
945 #ifdef HAVE_ISATTY
946 AioFile *af = Jim_CmdPrivData(interp);
947 Jim_SetResultInt(interp, isatty(fileno(af->fp)));
948 #else
949 Jim_SetResultInt(interp, 0);
950 #endif
952 return JIM_OK;
955 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
956 static int aio_cmd_recvfrom(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
958 AioFile *af = Jim_CmdPrivData(interp);
959 char *buf;
960 union sockaddr_any sa;
961 long len;
962 socklen_t salen = sizeof(sa);
963 int rlen;
965 if (Jim_GetLong(interp, argv[0], &len) != JIM_OK) {
966 return JIM_ERR;
969 buf = Jim_Alloc(len + 1);
971 rlen = recvfrom(fileno(af->fp), buf, len, 0, &sa.sa, &salen);
972 if (rlen < 0) {
973 Jim_Free(buf);
974 JimAioSetError(interp, NULL);
975 return JIM_ERR;
977 buf[rlen] = 0;
978 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, rlen));
980 if (argc > 1) {
981 return JimSetVariableSocketAddress(interp, argv[1], &sa, salen);
984 return JIM_OK;
988 static int aio_cmd_sendto(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
990 AioFile *af = Jim_CmdPrivData(interp);
991 int wlen;
992 int len;
993 const char *wdata;
994 union sockaddr_any sa;
995 const char *addr = Jim_String(argv[1]);
996 socklen_t salen;
998 if (JimParseSocketAddress(interp, af->addr_family, addr, &sa, &salen) != JIM_OK) {
999 return JIM_ERR;
1001 wdata = Jim_GetString(argv[0], &wlen);
1003 /* Note that we don't validate the socket type. Rely on sendto() failing if appropriate */
1004 len = sendto(fileno(af->fp), wdata, wlen, 0, &sa.sa, salen);
1005 if (len < 0) {
1006 JimAioSetError(interp, NULL);
1007 return JIM_ERR;
1009 Jim_SetResultInt(interp, len);
1010 return JIM_OK;
1013 static int aio_cmd_accept(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1015 AioFile *af = Jim_CmdPrivData(interp);
1016 int sock;
1017 union sockaddr_any sa;
1018 socklen_t salen = sizeof(sa);
1020 sock = accept(af->fd, &sa.sa, &salen);
1021 if (sock < 0) {
1022 JimAioSetError(interp, NULL);
1023 return JIM_ERR;
1026 if (argc > 0) {
1027 if (JimSetVariableSocketAddress(interp, argv[0], &sa, salen) != JIM_OK) {
1028 return JIM_ERR;
1032 /* Create the file command */
1033 return JimMakeChannel(interp, NULL, sock, Jim_NewStringObj(interp, "accept", -1),
1034 "aio.sockstream%ld", af->addr_family, "r+", AIO_NODELETE) ? JIM_OK : JIM_ERR;
1037 static int aio_cmd_sockname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1039 AioFile *af = Jim_CmdPrivData(interp);
1040 Jim_Obj *objPtr = aio_sockname(interp, af);
1042 if (objPtr == NULL) {
1043 JimAioSetError(interp, NULL);
1044 return JIM_ERR;
1046 Jim_SetResult(interp, objPtr);
1047 return JIM_OK;
1050 static int aio_cmd_peername(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1052 AioFile *af = Jim_CmdPrivData(interp);
1053 union sockaddr_any sa;
1054 socklen_t salen = sizeof(sa);
1056 if (getpeername(af->fd, &sa.sa, &salen) < 0) {
1057 JimAioSetError(interp, NULL);
1058 return JIM_ERR;
1060 Jim_SetResult(interp, JimFormatSocketAddress(interp, &sa, salen));
1061 return JIM_OK;
1064 static int aio_cmd_listen(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1066 AioFile *af = Jim_CmdPrivData(interp);
1067 long backlog;
1069 if (Jim_GetLong(interp, argv[0], &backlog) != JIM_OK) {
1070 return JIM_ERR;
1073 if (listen(af->fd, backlog)) {
1074 JimAioSetError(interp, NULL);
1075 return JIM_ERR;
1078 return JIM_OK;
1080 #endif /* JIM_BOOTSTRAP */
1082 static int aio_cmd_flush(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1084 AioFile *af = Jim_CmdPrivData(interp);
1086 if (fflush(af->fp) == EOF) {
1087 JimAioSetError(interp, af->filename);
1088 return JIM_ERR;
1090 return JIM_OK;
1093 static int aio_cmd_eof(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1095 AioFile *af = Jim_CmdPrivData(interp);
1097 Jim_SetResultInt(interp, !!af->fops->eof(af));
1098 return JIM_OK;
1101 static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1103 if (argc == 3) {
1104 int option = -1;
1105 #if defined(HAVE_SOCKETS)
1106 static const char * const options[] = { "r", "w", "-nodelete", NULL };
1107 enum { OPT_R, OPT_W, OPT_NODELETE };
1108 AioFile *af = Jim_CmdPrivData(interp);
1110 if (Jim_GetEnum(interp, argv[2], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
1111 return JIM_ERR;
1113 #endif
1114 switch (option) {
1115 #if defined(HAVE_SHUTDOWN)
1116 case OPT_R:
1117 case OPT_W:
1118 if (shutdown(af->fd, option == OPT_R ? SHUT_RD : SHUT_WR) == 0) {
1119 return JIM_OK;
1121 JimAioSetError(interp, NULL);
1122 return JIM_ERR;
1123 #endif
1124 #if UNIX_SOCKETS
1125 case OPT_NODELETE:
1126 if (af->addr_family == PF_UNIX) {
1127 af->flags |= AIO_NODELETE;
1128 break;
1130 /* fall through */
1131 #endif
1132 default:
1133 Jim_SetResultString(interp, "not supported", -1);
1134 return JIM_ERR;
1138 return Jim_DeleteCommand(interp, argv[0]);
1141 static int aio_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1143 AioFile *af = Jim_CmdPrivData(interp);
1144 int orig = SEEK_SET;
1145 jim_wide offset;
1147 if (argc == 2) {
1148 if (Jim_CompareStringImmediate(interp, argv[1], "start"))
1149 orig = SEEK_SET;
1150 else if (Jim_CompareStringImmediate(interp, argv[1], "current"))
1151 orig = SEEK_CUR;
1152 else if (Jim_CompareStringImmediate(interp, argv[1], "end"))
1153 orig = SEEK_END;
1154 else {
1155 return -1;
1158 if (Jim_GetWide(interp, argv[0], &offset) != JIM_OK) {
1159 return JIM_ERR;
1161 if (fseeko(af->fp, offset, orig) == -1) {
1162 JimAioSetError(interp, af->filename);
1163 return JIM_ERR;
1165 return JIM_OK;
1168 static int aio_cmd_tell(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1170 AioFile *af = Jim_CmdPrivData(interp);
1172 Jim_SetResultInt(interp, ftello(af->fp));
1173 return JIM_OK;
1176 static int aio_cmd_filename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1178 AioFile *af = Jim_CmdPrivData(interp);
1180 Jim_SetResult(interp, af->filename);
1181 return JIM_OK;
1184 #ifdef O_NDELAY
1185 static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1187 AioFile *af = Jim_CmdPrivData(interp);
1189 int fmode = fcntl(af->fd, F_GETFL);
1191 if (argc) {
1192 long nb;
1194 if (Jim_GetLong(interp, argv[0], &nb) != JIM_OK) {
1195 return JIM_ERR;
1197 if (nb) {
1198 fmode |= O_NDELAY;
1200 else {
1201 fmode &= ~O_NDELAY;
1203 (void)fcntl(af->fd, F_SETFL, fmode);
1205 Jim_SetResultInt(interp, (fmode & O_NONBLOCK) ? 1 : 0);
1206 return JIM_OK;
1208 #endif
1210 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
1211 #define SOCKOPT_BOOL 0
1212 #define SOCKOPT_INT 1
1213 #define SOCKOPT_TIMEVAL 2 /* not currently supported */
1215 static const struct sockopt_def {
1216 const char *name;
1217 int level;
1218 int opt;
1219 int type; /* SOCKOPT_xxx */
1220 } sockopts[] = {
1221 #ifdef SOL_SOCKET
1222 #ifdef SO_BROADCAST
1223 { "broadcast", SOL_SOCKET, SO_BROADCAST },
1224 #endif
1225 #ifdef SO_DEBUG
1226 { "debug", SOL_SOCKET, SO_DEBUG },
1227 #endif
1228 #ifdef SO_KEEPALIVE
1229 { "keepalive", SOL_SOCKET, SO_KEEPALIVE },
1230 #endif
1231 #ifdef SO_NOSIGPIPE
1232 { "nosigpipe", SOL_SOCKET, SO_NOSIGPIPE },
1233 #endif
1234 #ifdef SO_OOBINLINE
1235 { "oobinline", SOL_SOCKET, SO_OOBINLINE },
1236 #endif
1237 #ifdef SO_SNDBUF
1238 { "sndbuf", SOL_SOCKET, SO_SNDBUF, SOCKOPT_INT },
1239 #endif
1240 #ifdef SO_RCVBUF
1241 { "rcvbuf", SOL_SOCKET, SO_RCVBUF, SOCKOPT_INT },
1242 #endif
1243 #if 0 && defined(SO_SNDTIMEO)
1244 { "sndtimeo", SOL_SOCKET, SO_SNDTIMEO, SOCKOPT_TIMEVAL },
1245 #endif
1246 #if 0 && defined(SO_RCVTIMEO)
1247 { "rcvtimeo", SOL_SOCKET, SO_RCVTIMEO, SOCKOPT_TIMEVAL },
1248 #endif
1249 #endif
1250 #ifdef IPPROTO_TCP
1251 #ifdef TCP_NODELAY
1252 { "tcp_nodelay", IPPROTO_TCP, TCP_NODELAY },
1253 #endif
1254 #endif
1257 static int aio_cmd_sockopt(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1259 AioFile *af = Jim_CmdPrivData(interp);
1260 size_t i;
1262 if (argc == 0) {
1263 Jim_Obj *dictObjPtr = Jim_NewListObj(interp, NULL, 0);
1264 for (i = 0; i < sizeof(sockopts) / sizeof(*sockopts); i++) {
1265 int value = 0;
1266 socklen_t len = sizeof(value);
1267 if (getsockopt(af->fd, sockopts[i].level, sockopts[i].opt, (void *)&value, &len) == 0) {
1268 if (sockopts[i].type == SOCKOPT_BOOL) {
1269 value = !!value;
1271 Jim_ListAppendElement(interp, dictObjPtr, Jim_NewStringObj(interp, sockopts[i].name, -1));
1272 Jim_ListAppendElement(interp, dictObjPtr, Jim_NewIntObj(interp, value));
1275 Jim_SetResult(interp, dictObjPtr);
1276 return JIM_OK;
1278 if (argc == 1) {
1279 return -1;
1282 /* Set an option */
1283 for (i = 0; i < sizeof(sockopts) / sizeof(*sockopts); i++) {
1284 if (strcmp(Jim_String(argv[0]), sockopts[i].name) == 0) {
1285 int on;
1286 if (sockopts[i].type == SOCKOPT_BOOL) {
1287 if (Jim_GetBoolean(interp, argv[1], &on) != JIM_OK) {
1288 return JIM_ERR;
1291 else {
1292 long longval;
1293 if (Jim_GetLong(interp, argv[1], &longval) != JIM_OK) {
1294 return JIM_ERR;
1296 on = longval;
1298 if (setsockopt(af->fd, sockopts[i].level, sockopts[i].opt, (void *)&on, sizeof(on)) < 0) {
1299 Jim_SetResultFormatted(interp, "Failed to set %#s: %s", argv[0], strerror(errno));
1300 return JIM_ERR;
1302 return JIM_OK;
1305 /* Not found */
1306 Jim_SetResultFormatted(interp, "Unknown sockopt %#s", argv[0]);
1307 return JIM_ERR;
1309 #endif /* JIM_BOOTSTRAP */
1311 #ifdef HAVE_FSYNC
1312 static int aio_cmd_sync(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1314 AioFile *af = Jim_CmdPrivData(interp);
1316 fflush(af->fp);
1317 fsync(af->fd);
1318 return JIM_OK;
1320 #endif
1322 static int aio_cmd_buffering(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1324 AioFile *af = Jim_CmdPrivData(interp);
1326 static const char * const options[] = {
1327 "none",
1328 "line",
1329 "full",
1330 NULL
1332 enum
1334 OPT_NONE,
1335 OPT_LINE,
1336 OPT_FULL,
1338 int option;
1340 if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
1341 return JIM_ERR;
1343 switch (option) {
1344 case OPT_NONE:
1345 setvbuf(af->fp, NULL, _IONBF, 0);
1346 break;
1347 case OPT_LINE:
1348 setvbuf(af->fp, NULL, _IOLBF, BUFSIZ);
1349 break;
1350 case OPT_FULL:
1351 setvbuf(af->fp, NULL, _IOFBF, BUFSIZ);
1352 break;
1354 return JIM_OK;
1357 #ifdef jim_ext_eventloop
1358 static void JimAioFileEventFinalizer(Jim_Interp *interp, void *clientData)
1360 Jim_Obj **objPtrPtr = clientData;
1362 Jim_DecrRefCount(interp, *objPtrPtr);
1363 *objPtrPtr = NULL;
1366 static int JimAioFileEventHandler(Jim_Interp *interp, void *clientData, int mask)
1368 Jim_Obj **objPtrPtr = clientData;
1370 return Jim_EvalObjBackground(interp, *objPtrPtr);
1373 static int aio_eventinfo(Jim_Interp *interp, AioFile * af, unsigned mask, Jim_Obj **scriptHandlerObj,
1374 int argc, Jim_Obj * const *argv)
1376 if (argc == 0) {
1377 /* Return current script */
1378 if (*scriptHandlerObj) {
1379 Jim_SetResult(interp, *scriptHandlerObj);
1381 return JIM_OK;
1384 if (*scriptHandlerObj) {
1385 /* Delete old handler */
1386 Jim_DeleteFileHandler(interp, af->fd, mask);
1389 /* Now possibly add the new script(s) */
1390 if (Jim_Length(argv[0]) == 0) {
1391 /* Empty script, so done */
1392 return JIM_OK;
1395 /* A new script to add */
1396 Jim_IncrRefCount(argv[0]);
1397 *scriptHandlerObj = argv[0];
1399 Jim_CreateFileHandler(interp, af->fd, mask,
1400 JimAioFileEventHandler, scriptHandlerObj, JimAioFileEventFinalizer);
1402 return JIM_OK;
1405 static int aio_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1407 AioFile *af = Jim_CmdPrivData(interp);
1409 return aio_eventinfo(interp, af, JIM_EVENT_READABLE, &af->rEvent, argc, argv);
1412 static int aio_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1414 AioFile *af = Jim_CmdPrivData(interp);
1416 return aio_eventinfo(interp, af, JIM_EVENT_WRITABLE, &af->wEvent, argc, argv);
1419 static int aio_cmd_onexception(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1421 AioFile *af = Jim_CmdPrivData(interp);
1423 return aio_eventinfo(interp, af, JIM_EVENT_EXCEPTION, &af->eEvent, argc, argv);
1425 #endif
1427 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1428 static int aio_cmd_ssl(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1430 AioFile *af = Jim_CmdPrivData(interp);
1431 SSL *ssl;
1432 SSL_CTX *ssl_ctx;
1433 int server = 0;
1434 const char *sni = NULL;
1436 if (argc > 2) {
1437 static const char * const options[] = { "-server", "-sni", NULL };
1438 enum { OPT_SERVER, OPT_SNI };
1439 int option;
1441 if (Jim_GetEnum(interp, argv[2], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
1442 return JIM_ERR;
1444 switch (option) {
1445 case OPT_SERVER:
1446 if (argc != 4 && argc != 5) {
1447 return JIM_ERR;
1449 server = 1;
1450 break;
1452 case OPT_SNI:
1453 if (argc != 4) {
1454 return JIM_ERR;
1456 sni = Jim_String(argv[3]);
1457 break;
1461 if (af->ssl) {
1462 Jim_SetResultFormatted(interp, "%#s: stream is already ssl", argv[0]);
1463 return JIM_ERR;
1466 ssl_ctx = JimAioSslCtx(interp);
1467 if (ssl_ctx == NULL) {
1468 return JIM_ERR;
1471 ssl = SSL_new(ssl_ctx);
1472 if (ssl == NULL) {
1473 goto out;
1476 SSL_set_cipher_list(ssl, "ALL");
1478 if (SSL_set_fd(ssl, fileno(af->fp)) == 0) {
1479 goto out;
1482 if (server) {
1483 const char *certfile = Jim_String(argv[3]);
1484 const char *keyfile = (argc == 4) ? certfile : Jim_String(argv[4]);
1485 if (SSL_use_certificate_file(ssl, certfile, SSL_FILETYPE_PEM) != 1) {
1486 goto out;
1488 if (SSL_use_PrivateKey_file(ssl, keyfile, SSL_FILETYPE_PEM) != 1) {
1489 goto out;
1492 if (SSL_accept(ssl) != 1) {
1493 goto out;
1496 else {
1497 if (sni) {
1498 /* Set server name indication if requested */
1499 SSL_set_tlsext_host_name(ssl, sni);
1501 if (SSL_connect(ssl) != 1) {
1502 goto out;
1506 af->ssl = ssl;
1507 af->fops = &ssl_fops;
1509 /* Set the command name as the result */
1510 Jim_SetResult(interp, argv[0]);
1512 return JIM_OK;
1514 out:
1515 if (ssl) {
1516 SSL_free(ssl);
1518 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1519 return JIM_ERR;
1522 static int aio_cmd_verify(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1524 AioFile *af = Jim_CmdPrivData(interp);
1525 int ret;
1527 if (!af->fops->verify) {
1528 return JIM_OK;
1531 ret = af->fops->verify(af);
1532 if (ret != JIM_OK) {
1533 if (JimCheckStreamError(interp, af) == JIM_OK) {
1534 Jim_SetResultString(interp, "failed to verify the connection authenticity", -1);
1537 return ret;
1539 #endif /* JIM_BOOTSTRAP */
1541 #if defined(HAVE_STRUCT_FLOCK) && !defined(JIM_BOOTSTRAP)
1542 static int aio_cmd_lock(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1544 AioFile *af = Jim_CmdPrivData(interp);
1545 struct flock fl;
1546 int lockmode = F_SETLK;
1548 if (argc == 1) {
1549 if (!Jim_CompareStringImmediate(interp, argv[0], "-wait")) {
1550 return -1;
1552 lockmode = F_SETLKW;
1555 fl.l_start = 0;
1556 fl.l_len = 0;
1557 fl.l_type = F_WRLCK;
1558 fl.l_whence = SEEK_SET;
1560 switch (fcntl(af->fd, lockmode, &fl))
1562 case 0:
1563 Jim_SetResultInt(interp, 1);
1564 break;
1565 case -1:
1566 if (errno == EACCES || errno == EAGAIN)
1567 Jim_SetResultInt(interp, 0);
1568 else
1570 Jim_SetResultFormatted(interp, "lock failed: %s",
1571 strerror(errno));
1572 return JIM_ERR;
1574 break;
1575 default:
1576 Jim_SetResultInt(interp, 0);
1577 break;
1580 return JIM_OK;
1583 static int aio_cmd_unlock(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1585 AioFile *af = Jim_CmdPrivData(interp);
1586 struct flock fl;
1587 fl.l_start = 0;
1588 fl.l_len = 0;
1589 fl.l_type = F_UNLCK;
1590 fl.l_whence = SEEK_SET;
1592 Jim_SetResultInt(interp, fcntl(af->fd, F_SETLK, &fl) == 0);
1593 return JIM_OK;
1595 #endif /* JIM_BOOTSTRAP */
1597 #if defined(HAVE_TERMIOS_H) && !defined(JIM_BOOTSTRAP)
1598 static int aio_cmd_tty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1600 AioFile *af = Jim_CmdPrivData(interp);
1601 Jim_Obj *dictObjPtr;
1602 int ret;
1604 if (argc == 0) {
1605 /* get the current settings as a dictionary */
1606 dictObjPtr = Jim_GetTtySettings(interp, af->fd);
1607 if (dictObjPtr == NULL) {
1608 JimAioSetError(interp, NULL);
1609 return JIM_ERR;
1611 Jim_SetResult(interp, dictObjPtr);
1612 return JIM_OK;
1615 if (argc > 1) {
1616 /* Convert name value arguments to a dictionary */
1617 dictObjPtr = Jim_NewListObj(interp, argv, argc);
1619 else {
1620 /* The settings are already given as a list */
1621 dictObjPtr = argv[0];
1623 Jim_IncrRefCount(dictObjPtr);
1625 if (Jim_ListLength(interp, dictObjPtr) % 2) {
1626 /* Must be a valid dictionary */
1627 Jim_DecrRefCount(interp, dictObjPtr);
1628 return -1;
1631 ret = Jim_SetTtySettings(interp, af->fd, dictObjPtr);
1632 if (ret < 0) {
1633 JimAioSetError(interp, NULL);
1634 ret = JIM_ERR;
1636 Jim_DecrRefCount(interp, dictObjPtr);
1638 return ret;
1640 #endif /* JIM_BOOTSTRAP */
1642 static const jim_subcmd_type aio_command_table[] = {
1643 { "read",
1644 "?-nonewline|-pending|len?",
1645 aio_cmd_read,
1648 /* Description: Read and return bytes from the stream. To eof if no len. */
1650 { "copyto",
1651 "handle ?size?",
1652 aio_cmd_copy,
1655 /* Description: Copy up to 'size' bytes to the given filehandle, or to eof if no size. */
1657 { "getfd",
1658 NULL,
1659 aio_cmd_getfd,
1662 /* Description: Internal command to return the underlying file descriptor. */
1664 { "gets",
1665 "?var?",
1666 aio_cmd_gets,
1669 /* Description: Read one line and return it or store it in the var */
1671 { "puts",
1672 "?-nonewline? str",
1673 aio_cmd_puts,
1676 /* Description: Write the string, with newline unless -nonewline */
1678 { "isatty",
1679 NULL,
1680 aio_cmd_isatty,
1683 /* Description: Is the file descriptor a tty? */
1685 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
1686 { "recvfrom",
1687 "len ?addrvar?",
1688 aio_cmd_recvfrom,
1691 /* Description: Receive up to 'len' bytes on the socket. Sets 'addrvar' with receive address, if set */
1693 { "sendto",
1694 "str address",
1695 aio_cmd_sendto,
1698 /* Description: Send 'str' to the given address (dgram only) */
1700 { "accept",
1701 "?addrvar?",
1702 aio_cmd_accept,
1705 /* Description: Server socket only: Accept a connection and return stream */
1707 { "listen",
1708 "backlog",
1709 aio_cmd_listen,
1712 /* Description: Set the listen backlog for server socket */
1714 { "sockopt",
1715 "?opt 0|1?",
1716 aio_cmd_sockopt,
1719 /* Description: Return a dictionary of sockopts, or set the value of a sockopt */
1721 { "sockname",
1722 NULL,
1723 aio_cmd_sockname,
1726 /* Description: Returns the local address of the socket, if any */
1728 { "peername",
1729 NULL,
1730 aio_cmd_peername,
1733 /* Description: Returns the remote address of the socket, if any */
1735 #endif /* JIM_BOOTSTRAP */
1736 { "flush",
1737 NULL,
1738 aio_cmd_flush,
1741 /* Description: Flush the stream */
1743 { "eof",
1744 NULL,
1745 aio_cmd_eof,
1748 /* Description: Returns 1 if stream is at eof */
1750 { "close",
1751 "?r(ead)|w(rite)?",
1752 aio_cmd_close,
1755 JIM_MODFLAG_FULLARGV,
1756 /* Description: Closes the stream. */
1758 { "seek",
1759 "offset ?start|current|end",
1760 aio_cmd_seek,
1763 /* Description: Seeks in the stream (default 'current') */
1765 { "tell",
1766 NULL,
1767 aio_cmd_tell,
1770 /* Description: Returns the current seek position */
1772 { "filename",
1773 NULL,
1774 aio_cmd_filename,
1777 /* Description: Returns the original filename */
1779 #ifdef O_NDELAY
1780 { "ndelay",
1781 "?0|1?",
1782 aio_cmd_ndelay,
1785 /* Description: Set O_NDELAY (if arg). Returns current/new setting. */
1787 #endif
1788 #ifdef HAVE_FSYNC
1789 { "sync",
1790 NULL,
1791 aio_cmd_sync,
1794 /* Description: Flush and fsync() the stream */
1796 #endif
1797 { "buffering",
1798 "none|line|full",
1799 aio_cmd_buffering,
1802 /* Description: Sets buffering */
1804 #ifdef jim_ext_eventloop
1805 { "readable",
1806 "?readable-script?",
1807 aio_cmd_readable,
1810 /* Description: Returns script, or invoke readable-script when readable, {} to remove */
1812 { "writable",
1813 "?writable-script?",
1814 aio_cmd_writable,
1817 /* Description: Returns script, or invoke writable-script when writable, {} to remove */
1819 { "onexception",
1820 "?exception-script?",
1821 aio_cmd_onexception,
1824 /* Description: Returns script, or invoke exception-script when oob data, {} to remove */
1826 #endif
1827 #if !defined(JIM_BOOTSTRAP)
1828 #if defined(JIM_SSL)
1829 { "ssl",
1830 "?-server cert ?priv?|-sni servername?",
1831 aio_cmd_ssl,
1834 JIM_MODFLAG_FULLARGV
1835 /* Description: Wraps a stream socket with SSL/TLS and returns a new channel */
1837 { "verify",
1838 NULL,
1839 aio_cmd_verify,
1842 /* Description: Verifies the certificate of a SSL/TLS channel */
1844 #endif
1845 #if defined(HAVE_STRUCT_FLOCK)
1846 { "lock ?-wait?",
1847 NULL,
1848 aio_cmd_lock,
1851 /* Description: Attempt to get a lock, possibly waiting */
1853 { "unlock",
1854 NULL,
1855 aio_cmd_unlock,
1858 /* Description: Relase a lock. */
1860 #endif
1861 #if defined(HAVE_TERMIOS_H)
1862 { "tty",
1863 "?baud rate? ?data bits? ?stop bits? ?parity even|odd|none? ?handshake xonxoff|rtscts|none? ?input raw|cooked? ?output raw|cooked? ?echo 0|1? ?vmin n? ?vtime n?",
1864 aio_cmd_tty,
1867 /* Description: Get or set tty settings - valid only on a tty */
1869 #endif
1870 #endif /* JIM_BOOTSTRAP */
1871 { NULL }
1874 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1876 return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, aio_command_table, argc, argv), argc, argv);
1879 static int JimAioOpenCommand(Jim_Interp *interp, int argc,
1880 Jim_Obj *const *argv)
1882 const char *mode;
1884 if (argc != 2 && argc != 3) {
1885 Jim_WrongNumArgs(interp, 1, argv, "filename ?mode?");
1886 return JIM_ERR;
1889 mode = (argc == 3) ? Jim_String(argv[2]) : "r";
1891 #ifdef jim_ext_tclcompat
1893 const char *filename = Jim_String(argv[1]);
1895 /* If the filename starts with '|', use popen instead */
1896 if (*filename == '|') {
1897 Jim_Obj *evalObj[3];
1899 evalObj[0] = Jim_NewStringObj(interp, "::popen", -1);
1900 evalObj[1] = Jim_NewStringObj(interp, filename + 1, -1);
1901 evalObj[2] = Jim_NewStringObj(interp, mode, -1);
1903 return Jim_EvalObjVector(interp, 3, evalObj);
1906 #endif
1907 return JimMakeChannel(interp, NULL, -1, argv[1], "aio.handle%ld", 0, mode, 0) ? JIM_OK : JIM_ERR;
1910 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1911 static void JimAioSslContextDelProc(struct Jim_Interp *interp, void *privData)
1913 SSL_CTX_free((SSL_CTX *)privData);
1914 ERR_free_strings();
1917 #ifdef USE_TLSv1_2_method
1918 #define TLS_method TLSv1_2_method
1919 #endif
1921 static SSL_CTX *JimAioSslCtx(Jim_Interp *interp)
1923 SSL_CTX *ssl_ctx = (SSL_CTX *)Jim_GetAssocData(interp, "ssl_ctx");
1924 if (ssl_ctx == NULL) {
1925 SSL_load_error_strings();
1926 SSL_library_init();
1927 ssl_ctx = SSL_CTX_new(TLS_method());
1928 if (ssl_ctx && SSL_CTX_set_default_verify_paths(ssl_ctx)) {
1929 SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
1930 Jim_SetAssocData(interp, "ssl_ctx", JimAioSslContextDelProc, ssl_ctx);
1931 } else {
1932 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1935 return ssl_ctx;
1937 #endif /* JIM_BOOTSTRAP */
1940 * Creates a channel for fh/fd/filename.
1942 * If fh is not NULL, uses that as the channel (and sets AIO_KEEPOPEN).
1943 * Otherwise, if fd is >= 0, uses that as the channel.
1944 * Otherwise opens 'filename' with mode 'mode'.
1946 * hdlfmt is a sprintf format for the filehandle. Anything with %ld at the end will do.
1947 * mode is used for open or fdopen.
1949 * Creates the command and sets the name as the current result.
1950 * Returns the AioFile pointer on sucess or NULL on failure.
1952 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
1953 const char *hdlfmt, int family, const char *mode, int flags)
1955 AioFile *af;
1956 char buf[AIO_CMD_LEN];
1958 snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
1959 if (!filename) {
1960 filename = Jim_NewStringObj(interp, buf, -1);
1963 Jim_IncrRefCount(filename);
1965 if (fh == NULL) {
1966 if (fd >= 0) {
1967 #ifndef JIM_ANSIC
1968 fh = fdopen(fd, mode);
1969 #endif
1971 else
1972 fh = fopen(Jim_String(filename), mode);
1974 if (fh == NULL) {
1975 JimAioSetError(interp, filename);
1976 #ifndef JIM_ANSIC
1977 if (fd >= 0) {
1978 close(fd);
1980 #endif
1981 Jim_DecrRefCount(interp, filename);
1982 return NULL;
1986 /* Create the file command */
1987 af = Jim_Alloc(sizeof(*af));
1988 memset(af, 0, sizeof(*af));
1989 af->fp = fh;
1990 af->filename = filename;
1991 af->flags = flags;
1992 #ifndef JIM_ANSIC
1993 af->fd = fileno(fh);
1994 #ifdef FD_CLOEXEC
1995 if ((flags & AIO_KEEPOPEN) == 0) {
1996 (void)fcntl(af->fd, F_SETFD, FD_CLOEXEC);
1998 #endif
1999 #endif
2000 af->addr_family = family;
2001 af->fops = &stdio_fops;
2002 af->ssl = NULL;
2004 Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc);
2006 /* Note that the command must use the global namespace, even if
2007 * the current namespace is something different
2009 Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, Jim_NewStringObj(interp, buf, -1)));
2011 return af;
2014 #if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && UNIX_SOCKETS) || defined(HAVE_OPENPTY)
2016 * Create a pair of channels. e.g. from pipe() or socketpair()
2018 static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename,
2019 const char *hdlfmt, int family, const char * const mode[2])
2021 if (JimMakeChannel(interp, NULL, p[0], filename, hdlfmt, family, mode[0], 0)) {
2022 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
2023 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
2024 if (JimMakeChannel(interp, NULL, p[1], filename, hdlfmt, family, mode[1], 0)) {
2025 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
2026 Jim_SetResult(interp, objPtr);
2027 return JIM_OK;
2031 /* Can only be here if fdopen() failed */
2032 close(p[0]);
2033 close(p[1]);
2034 JimAioSetError(interp, NULL);
2035 return JIM_ERR;
2037 #endif
2039 #ifdef HAVE_PIPE
2040 static int JimAioPipeCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2042 int p[2];
2043 static const char * const mode[2] = { "r", "w" };
2045 if (argc != 1) {
2046 Jim_WrongNumArgs(interp, 1, argv, "");
2047 return JIM_ERR;
2050 if (pipe(p) != 0) {
2051 JimAioSetError(interp, NULL);
2052 return JIM_ERR;
2055 return JimMakeChannelPair(interp, p, argv[0], "aio.pipe%ld", 0, mode);
2057 #endif
2059 #ifdef HAVE_OPENPTY
2060 static int JimAioOpenPtyCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2062 int p[2];
2063 static const char * const mode[2] = { "r+", "w+" };
2065 if (argc != 1) {
2066 Jim_WrongNumArgs(interp, 1, argv, "");
2067 return JIM_ERR;
2070 if (openpty(&p[0], &p[1], NULL, NULL, NULL) != 0) {
2071 JimAioSetError(interp, NULL);
2072 return JIM_ERR;
2075 return JimMakeChannelPair(interp, p, argv[0], "aio.pty%ld", 0, mode);
2077 #endif
2079 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
2081 static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2083 const char *socktypes[] = {
2084 "unix",
2085 "unix.server",
2086 "unix.dgram",
2087 "unix.dgram.server",
2088 "dgram",
2089 "dgram.server",
2090 "stream",
2091 "stream.server",
2092 "pipe",
2093 "pair",
2094 "pty",
2095 NULL
2097 enum
2099 SOCK_UNIX,
2100 SOCK_UNIX_SERVER,
2101 SOCK_UNIX_DGRAM,
2102 SOCK_UNIX_DGRAM_SERVER,
2103 SOCK_DGRAM_CLIENT,
2104 SOCK_DGRAM_SERVER,
2105 SOCK_STREAM_CLIENT,
2106 SOCK_STREAM_SERVER,
2107 SOCK_STREAM_PIPE,
2108 SOCK_STREAM_SOCKETPAIR,
2109 SOCK_STREAM_PTY,
2111 int socktype;
2112 int sock;
2113 const char *addr = NULL;
2114 const char *bind_addr = NULL;
2115 const char *connect_addr = NULL;
2116 union sockaddr_any sa;
2117 socklen_t salen;
2118 int on = 1;
2119 int reuse = 0;
2120 int do_listen = 0;
2121 int family = PF_INET;
2122 int type = SOCK_STREAM;
2123 Jim_Obj *argv0 = argv[0];
2124 int ipv6 = 0;
2126 if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-ipv6")) {
2127 if (!IPV6) {
2128 Jim_SetResultString(interp, "ipv6 not supported", -1);
2129 return JIM_ERR;
2131 ipv6 = 1;
2132 family = PF_INET6;
2134 argc -= ipv6;
2135 argv += ipv6;
2137 if (argc < 2) {
2138 wrongargs:
2139 Jim_WrongNumArgs(interp, 1, &argv0, "?-ipv6? type ?address?");
2140 return JIM_ERR;
2143 if (Jim_GetEnum(interp, argv[1], socktypes, &socktype, "socket type", JIM_ERRMSG) != JIM_OK)
2144 return Jim_CheckShowCommands(interp, argv[1], socktypes);
2146 Jim_SetEmptyResult(interp);
2148 if (argc > 2) {
2149 addr = Jim_String(argv[2]);
2152 #if defined(HAVE_SOCKETPAIR) && UNIX_SOCKETS
2153 if (socktype == SOCK_STREAM_SOCKETPAIR) {
2154 int p[2];
2155 static const char * const mode[2] = { "r+", "r+" };
2157 if (addr || ipv6) {
2158 goto wrongargs;
2161 if (socketpair(PF_UNIX, SOCK_STREAM, 0, p) < 0) {
2162 JimAioSetError(interp, NULL);
2163 return JIM_ERR;
2165 return JimMakeChannelPair(interp, p, argv[1], "aio.sockpair%ld", PF_UNIX, mode);
2167 #endif
2169 #if defined(HAVE_PIPE)
2170 if (socktype == SOCK_STREAM_PIPE) {
2171 if (addr || ipv6) {
2172 goto wrongargs;
2174 return JimAioPipeCommand(interp, 1, &argv[1]);
2176 #endif
2178 /* Now all these socket types are very similar */
2179 switch (socktype) {
2180 case SOCK_DGRAM_CLIENT:
2181 connect_addr = addr;
2182 type = SOCK_DGRAM;
2183 break;
2185 case SOCK_STREAM_CLIENT:
2186 if (addr == NULL) {
2187 goto wrongargs;
2189 connect_addr = addr;
2190 break;
2192 case SOCK_STREAM_SERVER:
2193 if (addr == NULL) {
2194 goto wrongargs;
2196 bind_addr = addr;
2197 reuse = 1;
2198 do_listen = 1;
2199 break;
2201 case SOCK_DGRAM_SERVER:
2202 if (addr == NULL) {
2203 goto wrongargs;
2205 bind_addr = addr;
2206 type = SOCK_DGRAM;
2207 reuse = 1;
2208 break;
2210 #if UNIX_SOCKETS
2211 case SOCK_UNIX:
2212 if (addr == NULL) {
2213 goto wrongargs;
2215 connect_addr = addr;
2216 family = PF_UNIX;
2217 break;
2219 case SOCK_UNIX_DGRAM:
2220 connect_addr = addr;
2221 type = SOCK_DGRAM;
2222 family = PF_UNIX;
2223 /* A dgram unix domain socket client needs to bind
2224 * to a temporary address to allow the server to
2225 * send responses
2228 int tmpfd = Jim_MakeTempFile(interp, NULL, 1);
2229 if (tmpfd < 0) {
2230 return JIM_ERR;
2232 close(tmpfd);
2233 /* This will be valid until a result is next set, which is long enough here */
2234 bind_addr = Jim_String(Jim_GetResult(interp));
2236 break;
2238 case SOCK_UNIX_SERVER:
2239 if (addr == NULL) {
2240 goto wrongargs;
2242 bind_addr = addr;
2243 family = PF_UNIX;
2244 do_listen = 1;
2245 break;
2247 case SOCK_UNIX_DGRAM_SERVER:
2248 if (addr == NULL) {
2249 goto wrongargs;
2251 bind_addr = addr;
2252 type = SOCK_DGRAM;
2253 family = PF_UNIX;
2254 break;
2255 #endif
2256 #ifdef HAVE_OPENPTY
2257 case SOCK_STREAM_PTY:
2258 if (addr || ipv6) {
2259 goto wrongargs;
2261 return JimAioOpenPtyCommand(interp, 1, &argv[1]);
2262 #endif
2264 default:
2265 Jim_SetResultString(interp, "Unsupported socket type", -1);
2266 return JIM_ERR;
2269 /* Now do all the steps necessary for the given socket type */
2270 sock = socket(family, type, 0);
2271 if (sock < 0) {
2272 JimAioSetError(interp, NULL);
2273 return JIM_ERR;
2275 if (bind_addr) {
2276 if (JimParseSocketAddress(interp, family, bind_addr, &sa, &salen) != JIM_OK) {
2277 close(sock);
2278 return JIM_ERR;
2280 if (reuse) {
2281 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
2283 if (bind(sock, &sa.sa, salen)) {
2284 Jim_SetResultFormatted(interp, "%s: bind: %s", bind_addr, strerror(errno));
2285 close(sock);
2286 return JIM_ERR;
2289 if (connect_addr) {
2290 if (JimParseSocketAddress(interp, family, connect_addr, &sa, &salen) != JIM_OK) {
2291 close(sock);
2292 return JIM_ERR;
2294 if (connect(sock, &sa.sa, salen)) {
2295 Jim_SetResultFormatted(interp, "%s: connect: %s", connect_addr, strerror(errno));
2296 close(sock);
2297 return JIM_ERR;
2300 if (do_listen) {
2301 if (listen(sock, 5)) {
2302 Jim_SetResultFormatted(interp, "listen: %s", strerror(errno));
2303 close(sock);
2304 return JIM_ERR;
2308 return JimMakeChannel(interp, NULL, sock, argv[1], "aio.sock%ld", family, "r+", 0) ? JIM_OK : JIM_ERR;
2310 #endif /* JIM_BOOTSTRAP */
2312 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
2313 static int JimAioLoadSSLCertsCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2315 SSL_CTX *ssl_ctx;
2317 if (argc != 2) {
2318 Jim_WrongNumArgs(interp, 1, argv, "dir");
2319 return JIM_ERR;
2322 ssl_ctx = JimAioSslCtx(interp);
2323 if (!ssl_ctx) {
2324 return JIM_ERR;
2326 if (SSL_CTX_load_verify_locations(ssl_ctx, NULL, Jim_String(argv[1])) == 1) {
2327 return JIM_OK;
2329 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
2330 return JIM_ERR;
2332 #endif /* JIM_BOOTSTRAP */
2334 int Jim_aioInit(Jim_Interp *interp)
2336 if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG))
2337 return JIM_ERR;
2339 #if defined(JIM_SSL)
2340 Jim_CreateCommand(interp, "load_ssl_certs", JimAioLoadSSLCertsCommand, NULL, NULL);
2341 #endif
2343 Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL);
2344 #ifdef HAVE_SOCKETS
2345 Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL);
2346 #endif
2347 #ifdef HAVE_PIPE
2348 Jim_CreateCommand(interp, "pipe", JimAioPipeCommand, NULL, NULL);
2349 #endif
2351 /* Create filehandles for stdin, stdout and stderr */
2352 JimMakeChannel(interp, stdin, -1, NULL, "stdin", 0, "r", AIO_KEEPOPEN);
2353 JimMakeChannel(interp, stdout, -1, NULL, "stdout", 0, "w", AIO_KEEPOPEN);
2354 JimMakeChannel(interp, stderr, -1, NULL, "stderr", 0, "w", AIO_KEEPOPEN);
2356 return JIM_OK;