core: improvements to garbage collection
[jimtcl.git] / jim-aio.c
blob473aa7ec8f3b1cc3b320a45612723a34f1388844
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 if (ret == SSL_ERROR_SYSCALL || ret == 0) {
303 return stdio_error(af);
305 return JIM_ERR;
308 static const char *ssl_strerror(struct AioFile *af)
310 int err = ERR_get_error();
312 if (err) {
313 return ERR_error_string(err, NULL);
315 else {
316 return stdio_strerror(af);
320 static int ssl_verify(struct AioFile *af)
322 X509 *cert;
324 cert = SSL_get_peer_certificate(af->ssl);
325 if (!cert) {
326 return JIM_ERR;
328 X509_free(cert);
330 if (SSL_get_verify_result(af->ssl) == X509_V_OK) {
331 return JIM_OK;
334 return JIM_ERR;
337 static const JimAioFopsType ssl_fops = {
338 ssl_writer,
339 ssl_reader,
340 ssl_getline,
341 ssl_error,
342 ssl_strerror,
343 ssl_verify,
344 ssl_eof,
345 ssl_pending,
347 #endif /* JIM_BOOTSTRAP */
349 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
350 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
351 const char *hdlfmt, int family, const char *mode, int flags);
353 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
354 #ifndef HAVE_GETADDRINFO
356 * Poor man's getaddrinfo().
357 * hints->ai_family must be set and must be PF_INET or PF_INET6
358 * Only returns the first matching result.
359 * servname must be numeric.
361 struct addrinfo {
362 int ai_family;
363 socklen_t ai_addrlen;
364 struct sockaddr *ai_addr; /* simply points to ai_storage */
365 union sockaddr_any ai_storage;
368 static int getaddrinfo(const char *hostname, const char *servname,
369 const struct addrinfo *hints, struct addrinfo **res)
371 struct hostent *he;
372 char *end;
373 unsigned long port = strtoul(servname, &end, 10);
374 if (port == 0 || port > 65536 || *end) {
375 errno = EINVAL;
376 return -1;
379 if ((he = gethostbyname(hostname)) != NULL) {
380 int i;
381 for (i = 0; he->h_addr_list[i]; i++) {
382 if (he->h_addrtype == hints->ai_family) {
383 struct addrinfo *ai = malloc(sizeof(*ai));
384 memset(ai, 0, sizeof(*ai));
385 ai->ai_family = he->h_addrtype;
386 ai->ai_addr = &ai->ai_storage.sa;
387 if (ai->ai_family == PF_INET) {
388 ai->ai_addrlen = sizeof(ai->ai_storage.sin);
389 ai->ai_storage.sin.sin_family = he->h_addrtype;
390 assert(sizeof(ai->ai_storage.sin.sin_addr) == he->h_length);
391 memcpy(&ai->ai_storage.sin.sin_addr, he->h_addr_list[i], he->h_length);
392 ai->ai_storage.sin.sin_port = htons(port);
394 #if IPV6
395 else {
396 ai->ai_addrlen = sizeof(ai->ai_storage.sin6);
397 ai->ai_storage.sin6.sin6_family = he->h_addrtype;
398 assert(sizeof(ai->ai_storage.sin6.sin6_addr) == he->h_length);
399 memcpy(&ai->ai_storage.sin6.sin6_addr, he->h_addr_list[i], he->h_length);
400 ai->ai_storage.sin6.sin6_port = htons(port);
402 #endif
403 *res = ai;
404 return 0;
408 return ENOENT;
411 static void freeaddrinfo(struct addrinfo *ai)
413 free(ai);
415 #endif
417 static int JimParseIPv6Address(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, socklen_t *salen)
419 #if IPV6
421 * An IPv6 addr/port looks like:
422 * [::1]
423 * [::1]:2000
424 * [fe80::223:6cff:fe95:bdc0%en1]:2000
425 * [::]:2000
426 * 2000
428 * Note that the "any" address is ::, which is the same as when no address is specified.
430 char *sthost = NULL;
431 const char *stport;
432 int ret = JIM_OK;
433 struct addrinfo req;
434 struct addrinfo *ai;
436 stport = strrchr(hostport, ':');
437 if (!stport) {
438 /* No : so, the whole thing is the port */
439 stport = hostport;
440 hostport = "::";
441 sthost = Jim_StrDup(hostport);
443 else {
444 stport++;
447 if (*hostport == '[') {
448 /* This is a numeric ipv6 address */
449 char *pt = strchr(++hostport, ']');
450 if (pt) {
451 sthost = Jim_StrDupLen(hostport, pt - hostport);
455 if (!sthost) {
456 sthost = Jim_StrDupLen(hostport, stport - hostport - 1);
459 memset(&req, '\0', sizeof(req));
460 req.ai_family = PF_INET6;
462 if (getaddrinfo(sthost, stport, &req, &ai)) {
463 Jim_SetResultFormatted(interp, "Not a valid address: %s:%s", sthost, stport);
464 ret = JIM_ERR;
466 else {
467 memcpy(&sa->sin6, ai->ai_addr, ai->ai_addrlen);
468 *salen = ai->ai_addrlen;
469 freeaddrinfo(ai);
471 Jim_Free(sthost);
473 return ret;
474 #else
475 Jim_SetResultString(interp, "ipv6 not supported", -1);
476 return JIM_ERR;
477 #endif
480 static int JimParseIpAddress(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, socklen_t *salen)
482 /* An IPv4 addr/port looks like:
483 * 192.168.1.5
484 * 192.168.1.5:2000
485 * 2000
487 * If the address is missing, INADDR_ANY is used.
488 * If the port is missing, 0 is used (only useful for server sockets).
490 char *sthost = NULL;
491 const char *stport;
492 int ret = JIM_OK;
493 struct addrinfo req;
494 struct addrinfo *ai;
496 stport = strrchr(hostport, ':');
497 if (!stport) {
498 /* No : so, the whole thing is the port */
499 stport = hostport;
500 sthost = Jim_StrDup("0.0.0.0");
502 else {
503 sthost = Jim_StrDupLen(hostport, stport - hostport);
504 stport++;
507 memset(&req, '\0', sizeof(req));
508 req.ai_family = PF_INET;
510 if (getaddrinfo(sthost, stport, &req, &ai)) {
511 ret = JIM_ERR;
513 else {
514 memcpy(&sa->sin, ai->ai_addr, ai->ai_addrlen);
515 *salen = ai->ai_addrlen;
516 freeaddrinfo(ai);
518 Jim_Free(sthost);
520 if (ret != JIM_OK) {
521 Jim_SetResultFormatted(interp, "Not a valid address: %s", hostport);
524 return ret;
527 #if UNIX_SOCKETS
528 static int JimParseDomainAddress(Jim_Interp *interp, const char *path, union sockaddr_any *sa, socklen_t *salen)
530 sa->sun.sun_family = PF_UNIX;
531 snprintf(sa->sun.sun_path, sizeof(sa->sun.sun_path), "%s", path);
532 *salen = strlen(sa->sun.sun_path) + 1 + sizeof(sa->sun.sun_family);
534 return JIM_OK;
536 #endif
538 static int JimParseSocketAddress(Jim_Interp *interp, int family, const char *addr, union sockaddr_any *sa, socklen_t *salen)
540 switch (family) {
541 #if UNIX_SOCKETS
542 case PF_UNIX:
543 return JimParseDomainAddress(interp, addr, sa, salen);
544 #endif
545 case PF_INET6:
546 return JimParseIPv6Address(interp, addr, sa, salen);
547 case PF_INET:
548 return JimParseIpAddress(interp, addr, sa, salen);
550 return JIM_ERR;
554 * Format that address in 'sa' as a string and return it as a zero-refcount object.
557 static Jim_Obj *JimFormatSocketAddress(Jim_Interp *interp, const union sockaddr_any *sa, socklen_t salen)
559 /* INET6_ADDRSTRLEN is 46. Add some for [] and port */
560 char addrbuf[60];
561 const char *addr = addrbuf;
562 int addrlen = -1;
564 switch (sa->sa.sa_family) {
565 #if UNIX_SOCKETS
566 case PF_UNIX:
567 addr = sa->sun.sun_path;
568 addrlen = salen - 1 - sizeof(sa->sun.sun_family);
569 if (addrlen < 0) {
570 addrlen = 0;
572 break;
573 #endif
574 #if IPV6
575 case PF_INET6:
576 addrbuf[0] = '[';
577 /* Allow 9 for []:65535\0 */
578 inet_ntop(sa->sa.sa_family, &sa->sin6.sin6_addr, addrbuf + 1, sizeof(addrbuf) - 9);
579 snprintf(addrbuf + strlen(addrbuf), 8, "]:%d", ntohs(sa->sin6.sin6_port));
580 break;
581 #endif
582 case PF_INET:
583 /* Allow 7 for :65535\0 */
584 inet_ntop(sa->sa.sa_family, &sa->sin.sin_addr, addrbuf, sizeof(addrbuf) - 7);
585 snprintf(addrbuf + strlen(addrbuf), 7, ":%d", ntohs(sa->sin.sin_port));
586 break;
588 default:
589 /* Otherwise just an empty address */
590 addr = "";
591 break;
594 return Jim_NewStringObj(interp, addr, addrlen);
597 static int JimSetVariableSocketAddress(Jim_Interp *interp, Jim_Obj *varObjPtr, const union sockaddr_any *sa, socklen_t salen)
599 int ret;
600 Jim_Obj *objPtr = JimFormatSocketAddress(interp, sa, salen);
601 Jim_IncrRefCount(objPtr);
602 ret = Jim_SetVariable(interp, varObjPtr, objPtr);
603 Jim_DecrRefCount(interp, objPtr);
604 return ret;
607 static Jim_Obj *aio_sockname(Jim_Interp *interp, AioFile *af)
609 union sockaddr_any sa;
610 socklen_t salen = sizeof(sa);
612 if (getsockname(af->fd, &sa.sa, &salen) < 0) {
613 return NULL;
615 return JimFormatSocketAddress(interp, &sa, salen);
617 #endif /* JIM_BOOTSTRAP */
619 static const char *JimAioErrorString(AioFile *af)
621 if (af && af->fops)
622 return af->fops->strerror(af);
624 return strerror(errno);
627 static void JimAioSetError(Jim_Interp *interp, Jim_Obj *name)
629 AioFile *af = Jim_CmdPrivData(interp);
631 if (name) {
632 Jim_SetResultFormatted(interp, "%#s: %s", name, JimAioErrorString(af));
634 else {
635 Jim_SetResultString(interp, JimAioErrorString(af), -1);
639 static int JimCheckStreamError(Jim_Interp *interp, AioFile *af)
641 int ret = af->fops->error(af);
642 if (ret) {
643 JimAioSetError(interp, af->filename);
645 return ret;
648 static void JimAioDelProc(Jim_Interp *interp, void *privData)
650 AioFile *af = privData;
652 JIM_NOTUSED(interp);
654 #if UNIX_SOCKETS
655 if (af->addr_family == PF_UNIX && (af->flags & AIO_NODELETE) == 0) {
656 /* If this is bound, delete the socket file now */
657 Jim_Obj *filenameObj = aio_sockname(interp, af);
658 if (filenameObj) {
659 if (Jim_Length(filenameObj)) {
660 remove(Jim_String(filenameObj));
662 Jim_FreeNewObj(interp, filenameObj);
665 #endif
667 Jim_DecrRefCount(interp, af->filename);
669 #ifdef jim_ext_eventloop
670 /* remove all existing EventHandlers */
671 Jim_DeleteFileHandler(interp, af->fd, JIM_EVENT_READABLE | JIM_EVENT_WRITABLE | JIM_EVENT_EXCEPTION);
672 #endif
674 #if defined(JIM_SSL)
675 if (af->ssl != NULL) {
676 SSL_free(af->ssl);
678 #endif
679 if (!(af->flags & AIO_KEEPOPEN)) {
680 fclose(af->fp);
683 Jim_Free(af);
686 static int aio_cmd_read(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
688 AioFile *af = Jim_CmdPrivData(interp);
689 char buf[AIO_BUF_LEN];
690 Jim_Obj *objPtr;
691 int nonewline = 0;
692 int pending = 0;
693 jim_wide neededLen = -1; /* -1 is "read as much as possible" */
694 static const char * const options[] = { "-pending", "-nonewline", NULL };
695 enum { OPT_PENDING, OPT_NONEWLINE };
696 int option;
698 if (argc) {
699 if (*Jim_String(argv[0]) == '-') {
700 if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
701 return JIM_ERR;
703 switch (option) {
704 case OPT_PENDING:
705 if (!af->fops->pending) {
706 Jim_SetResultString(interp, "-pending not supported on this connection type", -1);
707 return JIM_ERR;
709 pending++;
710 break;
711 case OPT_NONEWLINE:
712 nonewline++;
713 break;
716 else {
717 if (Jim_GetWide(interp, argv[0], &neededLen) != JIM_OK)
718 return JIM_ERR;
719 if (neededLen < 0) {
720 Jim_SetResultString(interp, "invalid parameter: negative len", -1);
721 return JIM_ERR;
724 argc--;
725 argv++;
727 if (argc) {
728 return -1;
730 objPtr = Jim_NewStringObj(interp, NULL, 0);
731 while (neededLen != 0) {
732 int retval;
733 int readlen;
735 if (neededLen == -1) {
736 readlen = AIO_BUF_LEN;
738 else {
739 readlen = (neededLen > AIO_BUF_LEN ? AIO_BUF_LEN : neededLen);
741 retval = af->fops->reader(af, buf, pending ? 1 : readlen);
742 if (retval > 0) {
743 Jim_AppendString(interp, objPtr, buf, retval);
744 if (neededLen != -1) {
745 neededLen -= retval;
747 else if (pending) {
748 /* If pending was specified, after we do the initial read,
749 * we do a second read to fetch any buffered data
751 neededLen = af->fops->pending(af);
754 if (retval <= 0) {
755 break;
758 /* Check for error conditions */
759 if (JimCheckStreamError(interp, af)) {
760 Jim_FreeNewObj(interp, objPtr);
761 return JIM_ERR;
763 if (nonewline) {
764 int len;
765 const char *s = Jim_GetString(objPtr, &len);
767 if (len > 0 && s[len - 1] == '\n') {
768 objPtr->length--;
769 objPtr->bytes[objPtr->length] = '\0';
772 Jim_SetResult(interp, objPtr);
773 return JIM_OK;
776 AioFile *Jim_AioFile(Jim_Interp *interp, Jim_Obj *command)
778 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, command, JIM_ERRMSG);
780 /* XXX: There ought to be a supported API for this */
781 if (cmdPtr && !cmdPtr->isproc && cmdPtr->u.native.cmdProc == JimAioSubCmdProc) {
782 return (AioFile *) cmdPtr->u.native.privData;
784 Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", command);
785 return NULL;
788 FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command)
790 AioFile *af;
792 af = Jim_AioFile(interp, command);
793 if (af == NULL) {
794 return NULL;
797 return af->fp;
800 static int aio_cmd_getfd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
802 AioFile *af = Jim_CmdPrivData(interp);
804 fflush(af->fp);
805 Jim_SetResultInt(interp, fileno(af->fp));
807 return JIM_OK;
810 static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
812 AioFile *af = Jim_CmdPrivData(interp);
813 jim_wide count = 0;
814 jim_wide maxlen = JIM_WIDE_MAX;
815 AioFile *outf = Jim_AioFile(interp, argv[0]);
817 if (outf == NULL) {
818 return JIM_ERR;
821 if (argc == 2) {
822 if (Jim_GetWide(interp, argv[1], &maxlen) != JIM_OK) {
823 return JIM_ERR;
827 while (count < maxlen) {
828 /* A reasonable compromise between stack size and speed */
829 char buf[AIO_BUF_LEN];
830 jim_wide len = maxlen - count;
831 if (len > sizeof(buf)) {
832 len = sizeof(buf);
835 len = af->fops->reader(af, buf, len);
836 if (len <= 0) {
837 break;
839 if (outf->fops->writer(outf, buf, len) != len) {
840 break;
842 count += len;
845 if (JimCheckStreamError(interp, af) || JimCheckStreamError(interp, outf)) {
846 return JIM_ERR;
849 Jim_SetResultInt(interp, count);
851 return JIM_OK;
854 static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
856 AioFile *af = Jim_CmdPrivData(interp);
857 char buf[AIO_BUF_LEN];
858 Jim_Obj *objPtr;
859 int len;
861 errno = 0;
863 objPtr = Jim_NewStringObj(interp, NULL, 0);
864 while (1) {
865 buf[AIO_BUF_LEN - 1] = '_';
867 if (af->fops->getline(af, buf, AIO_BUF_LEN) == NULL)
868 break;
870 if (buf[AIO_BUF_LEN - 1] == '\0' && buf[AIO_BUF_LEN - 2] != '\n') {
871 Jim_AppendString(interp, objPtr, buf, AIO_BUF_LEN - 1);
873 else {
874 len = strlen(buf);
876 if (len && (buf[len - 1] == '\n')) {
877 /* strip "\n" */
878 len--;
881 Jim_AppendString(interp, objPtr, buf, len);
882 break;
886 if (JimCheckStreamError(interp, af)) {
887 /* I/O error */
888 Jim_FreeNewObj(interp, objPtr);
889 return JIM_ERR;
892 if (argc) {
893 if (Jim_SetVariable(interp, argv[0], objPtr) != JIM_OK) {
894 Jim_FreeNewObj(interp, objPtr);
895 return JIM_ERR;
898 len = Jim_Length(objPtr);
900 if (len == 0 && af->fops->eof(af)) {
901 /* On EOF returns -1 if varName was specified */
902 len = -1;
904 Jim_SetResultInt(interp, len);
906 else {
907 Jim_SetResult(interp, objPtr);
909 return JIM_OK;
912 static int aio_cmd_puts(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
914 AioFile *af = Jim_CmdPrivData(interp);
915 int wlen;
916 const char *wdata;
917 Jim_Obj *strObj;
919 if (argc == 2) {
920 if (!Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
921 return -1;
923 strObj = argv[1];
925 else {
926 strObj = argv[0];
929 wdata = Jim_GetString(strObj, &wlen);
930 if (af->fops->writer(af, wdata, wlen) == wlen) {
931 if (argc == 2 || af->fops->writer(af, "\n", 1) == 1) {
932 return JIM_OK;
935 JimAioSetError(interp, af->filename);
936 return JIM_ERR;
939 static int aio_cmd_isatty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
941 #ifdef HAVE_ISATTY
942 AioFile *af = Jim_CmdPrivData(interp);
943 Jim_SetResultInt(interp, isatty(fileno(af->fp)));
944 #else
945 Jim_SetResultInt(interp, 0);
946 #endif
948 return JIM_OK;
951 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
952 static int aio_cmd_recvfrom(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
954 AioFile *af = Jim_CmdPrivData(interp);
955 char *buf;
956 union sockaddr_any sa;
957 long len;
958 socklen_t salen = sizeof(sa);
959 int rlen;
961 if (Jim_GetLong(interp, argv[0], &len) != JIM_OK) {
962 return JIM_ERR;
965 buf = Jim_Alloc(len + 1);
967 rlen = recvfrom(fileno(af->fp), buf, len, 0, &sa.sa, &salen);
968 if (rlen < 0) {
969 Jim_Free(buf);
970 JimAioSetError(interp, NULL);
971 return JIM_ERR;
973 buf[rlen] = 0;
974 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, rlen));
976 if (argc > 1) {
977 return JimSetVariableSocketAddress(interp, argv[1], &sa, salen);
980 return JIM_OK;
984 static int aio_cmd_sendto(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
986 AioFile *af = Jim_CmdPrivData(interp);
987 int wlen;
988 int len;
989 const char *wdata;
990 union sockaddr_any sa;
991 const char *addr = Jim_String(argv[1]);
992 socklen_t salen;
994 if (JimParseSocketAddress(interp, af->addr_family, addr, &sa, &salen) != JIM_OK) {
995 return JIM_ERR;
997 wdata = Jim_GetString(argv[0], &wlen);
999 /* Note that we don't validate the socket type. Rely on sendto() failing if appropriate */
1000 len = sendto(fileno(af->fp), wdata, wlen, 0, &sa.sa, salen);
1001 if (len < 0) {
1002 JimAioSetError(interp, NULL);
1003 return JIM_ERR;
1005 Jim_SetResultInt(interp, len);
1006 return JIM_OK;
1009 static int aio_cmd_accept(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1011 AioFile *af = Jim_CmdPrivData(interp);
1012 int sock;
1013 union sockaddr_any sa;
1014 socklen_t salen = sizeof(sa);
1016 sock = accept(af->fd, &sa.sa, &salen);
1017 if (sock < 0) {
1018 JimAioSetError(interp, NULL);
1019 return JIM_ERR;
1022 if (argc > 0) {
1023 if (JimSetVariableSocketAddress(interp, argv[0], &sa, salen) != JIM_OK) {
1024 return JIM_ERR;
1028 /* Create the file command */
1029 return JimMakeChannel(interp, NULL, sock, Jim_NewStringObj(interp, "accept", -1),
1030 "aio.sockstream%ld", af->addr_family, "r+", AIO_NODELETE) ? JIM_OK : JIM_ERR;
1033 static int aio_cmd_sockname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1035 AioFile *af = Jim_CmdPrivData(interp);
1036 Jim_Obj *objPtr = aio_sockname(interp, af);
1038 if (objPtr == NULL) {
1039 JimAioSetError(interp, NULL);
1040 return JIM_ERR;
1042 Jim_SetResult(interp, objPtr);
1043 return JIM_OK;
1046 static int aio_cmd_peername(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1048 AioFile *af = Jim_CmdPrivData(interp);
1049 union sockaddr_any sa;
1050 socklen_t salen = sizeof(sa);
1052 if (getpeername(af->fd, &sa.sa, &salen) < 0) {
1053 JimAioSetError(interp, NULL);
1054 return JIM_ERR;
1056 Jim_SetResult(interp, JimFormatSocketAddress(interp, &sa, salen));
1057 return JIM_OK;
1060 static int aio_cmd_listen(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1062 AioFile *af = Jim_CmdPrivData(interp);
1063 long backlog;
1065 if (Jim_GetLong(interp, argv[0], &backlog) != JIM_OK) {
1066 return JIM_ERR;
1069 if (listen(af->fd, backlog)) {
1070 JimAioSetError(interp, NULL);
1071 return JIM_ERR;
1074 return JIM_OK;
1076 #endif /* JIM_BOOTSTRAP */
1078 static int aio_cmd_flush(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1080 AioFile *af = Jim_CmdPrivData(interp);
1082 if (fflush(af->fp) == EOF) {
1083 JimAioSetError(interp, af->filename);
1084 return JIM_ERR;
1086 return JIM_OK;
1089 static int aio_cmd_eof(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1091 AioFile *af = Jim_CmdPrivData(interp);
1093 Jim_SetResultInt(interp, !!af->fops->eof(af));
1094 return JIM_OK;
1097 static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1099 if (argc == 3) {
1100 int option = -1;
1101 #if defined(HAVE_SOCKETS)
1102 static const char * const options[] = { "r", "w", "-nodelete", NULL };
1103 enum { OPT_R, OPT_W, OPT_NODELETE };
1104 AioFile *af = Jim_CmdPrivData(interp);
1106 if (Jim_GetEnum(interp, argv[2], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
1107 return JIM_ERR;
1109 #endif
1110 switch (option) {
1111 #if defined(HAVE_SHUTDOWN)
1112 case OPT_R:
1113 case OPT_W:
1114 if (shutdown(af->fd, option == OPT_R ? SHUT_RD : SHUT_WR) == 0) {
1115 return JIM_OK;
1117 JimAioSetError(interp, NULL);
1118 return JIM_ERR;
1119 #endif
1120 #if UNIX_SOCKETS
1121 case OPT_NODELETE:
1122 if (af->addr_family == PF_UNIX) {
1123 af->flags |= AIO_NODELETE;
1124 break;
1126 /* fall through */
1127 #endif
1128 default:
1129 Jim_SetResultString(interp, "not supported", -1);
1130 return JIM_ERR;
1134 return Jim_DeleteCommand(interp, argv[0]);
1137 static int aio_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1139 AioFile *af = Jim_CmdPrivData(interp);
1140 int orig = SEEK_SET;
1141 jim_wide offset;
1143 if (argc == 2) {
1144 if (Jim_CompareStringImmediate(interp, argv[1], "start"))
1145 orig = SEEK_SET;
1146 else if (Jim_CompareStringImmediate(interp, argv[1], "current"))
1147 orig = SEEK_CUR;
1148 else if (Jim_CompareStringImmediate(interp, argv[1], "end"))
1149 orig = SEEK_END;
1150 else {
1151 return -1;
1154 if (Jim_GetWide(interp, argv[0], &offset) != JIM_OK) {
1155 return JIM_ERR;
1157 if (fseeko(af->fp, offset, orig) == -1) {
1158 JimAioSetError(interp, af->filename);
1159 return JIM_ERR;
1161 return JIM_OK;
1164 static int aio_cmd_tell(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1166 AioFile *af = Jim_CmdPrivData(interp);
1168 Jim_SetResultInt(interp, ftello(af->fp));
1169 return JIM_OK;
1172 static int aio_cmd_filename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1174 AioFile *af = Jim_CmdPrivData(interp);
1176 Jim_SetResult(interp, af->filename);
1177 return JIM_OK;
1180 #ifdef O_NDELAY
1181 static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1183 AioFile *af = Jim_CmdPrivData(interp);
1185 int fmode = fcntl(af->fd, F_GETFL);
1187 if (argc) {
1188 long nb;
1190 if (Jim_GetLong(interp, argv[0], &nb) != JIM_OK) {
1191 return JIM_ERR;
1193 if (nb) {
1194 fmode |= O_NDELAY;
1196 else {
1197 fmode &= ~O_NDELAY;
1199 (void)fcntl(af->fd, F_SETFL, fmode);
1201 Jim_SetResultInt(interp, (fmode & O_NONBLOCK) ? 1 : 0);
1202 return JIM_OK;
1204 #endif
1206 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
1207 #define SOCKOPT_BOOL 0
1208 #define SOCKOPT_INT 1
1209 #define SOCKOPT_TIMEVAL 2 /* not currently supported */
1211 static const struct sockopt_def {
1212 const char *name;
1213 int level;
1214 int opt;
1215 int type; /* SOCKOPT_xxx */
1216 } sockopts[] = {
1217 #ifdef SOL_SOCKET
1218 #ifdef SO_BROADCAST
1219 { "broadcast", SOL_SOCKET, SO_BROADCAST },
1220 #endif
1221 #ifdef SO_DEBUG
1222 { "debug", SOL_SOCKET, SO_DEBUG },
1223 #endif
1224 #ifdef SO_KEEPALIVE
1225 { "keepalive", SOL_SOCKET, SO_KEEPALIVE },
1226 #endif
1227 #ifdef SO_NOSIGPIPE
1228 { "nosigpipe", SOL_SOCKET, SO_NOSIGPIPE },
1229 #endif
1230 #ifdef SO_OOBINLINE
1231 { "oobinline", SOL_SOCKET, SO_OOBINLINE },
1232 #endif
1233 #ifdef SO_SNDBUF
1234 { "sndbuf", SOL_SOCKET, SO_SNDBUF, SOCKOPT_INT },
1235 #endif
1236 #ifdef SO_RCVBUF
1237 { "rcvbuf", SOL_SOCKET, SO_RCVBUF, SOCKOPT_INT },
1238 #endif
1239 #if 0 && defined(SO_SNDTIMEO)
1240 { "sndtimeo", SOL_SOCKET, SO_SNDTIMEO, SOCKOPT_TIMEVAL },
1241 #endif
1242 #if 0 && defined(SO_RCVTIMEO)
1243 { "rcvtimeo", SOL_SOCKET, SO_RCVTIMEO, SOCKOPT_TIMEVAL },
1244 #endif
1245 #endif
1246 #ifdef IPPROTO_TCP
1247 #ifdef TCP_NODELAY
1248 { "tcp_nodelay", IPPROTO_TCP, TCP_NODELAY },
1249 #endif
1250 #endif
1253 static int aio_cmd_sockopt(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1255 AioFile *af = Jim_CmdPrivData(interp);
1256 size_t i;
1258 if (argc == 0) {
1259 Jim_Obj *dictObjPtr = Jim_NewListObj(interp, NULL, 0);
1260 for (i = 0; i < sizeof(sockopts) / sizeof(*sockopts); i++) {
1261 int value = 0;
1262 socklen_t len = sizeof(value);
1263 if (getsockopt(af->fd, sockopts[i].level, sockopts[i].opt, (void *)&value, &len) == 0) {
1264 if (sockopts[i].type == SOCKOPT_BOOL) {
1265 value = !!value;
1267 Jim_ListAppendElement(interp, dictObjPtr, Jim_NewStringObj(interp, sockopts[i].name, -1));
1268 Jim_ListAppendElement(interp, dictObjPtr, Jim_NewIntObj(interp, value));
1271 Jim_SetResult(interp, dictObjPtr);
1272 return JIM_OK;
1274 if (argc == 1) {
1275 return -1;
1278 /* Set an option */
1279 for (i = 0; i < sizeof(sockopts) / sizeof(*sockopts); i++) {
1280 if (strcmp(Jim_String(argv[0]), sockopts[i].name) == 0) {
1281 int on;
1282 if (sockopts[i].type == SOCKOPT_BOOL) {
1283 if (Jim_GetBoolean(interp, argv[1], &on) != JIM_OK) {
1284 return JIM_ERR;
1287 else {
1288 long longval;
1289 if (Jim_GetLong(interp, argv[1], &longval) != JIM_OK) {
1290 return JIM_ERR;
1292 on = longval;
1294 if (setsockopt(af->fd, sockopts[i].level, sockopts[i].opt, (void *)&on, sizeof(on)) < 0) {
1295 Jim_SetResultFormatted(interp, "Failed to set %#s: %s", argv[0], strerror(errno));
1296 return JIM_ERR;
1298 return JIM_OK;
1301 /* Not found */
1302 Jim_SetResultFormatted(interp, "Unknown sockopt %#s", argv[0]);
1303 return JIM_ERR;
1305 #endif /* JIM_BOOTSTRAP */
1307 #ifdef HAVE_FSYNC
1308 static int aio_cmd_sync(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1310 AioFile *af = Jim_CmdPrivData(interp);
1312 fflush(af->fp);
1313 fsync(af->fd);
1314 return JIM_OK;
1316 #endif
1318 static int aio_cmd_buffering(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1320 AioFile *af = Jim_CmdPrivData(interp);
1322 static const char * const options[] = {
1323 "none",
1324 "line",
1325 "full",
1326 NULL
1328 enum
1330 OPT_NONE,
1331 OPT_LINE,
1332 OPT_FULL,
1334 int option;
1336 if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
1337 return JIM_ERR;
1339 switch (option) {
1340 case OPT_NONE:
1341 setvbuf(af->fp, NULL, _IONBF, 0);
1342 break;
1343 case OPT_LINE:
1344 setvbuf(af->fp, NULL, _IOLBF, BUFSIZ);
1345 break;
1346 case OPT_FULL:
1347 setvbuf(af->fp, NULL, _IOFBF, BUFSIZ);
1348 break;
1350 return JIM_OK;
1353 #ifdef jim_ext_eventloop
1354 static void JimAioFileEventFinalizer(Jim_Interp *interp, void *clientData)
1356 Jim_Obj **objPtrPtr = clientData;
1358 Jim_DecrRefCount(interp, *objPtrPtr);
1359 *objPtrPtr = NULL;
1362 static int JimAioFileEventHandler(Jim_Interp *interp, void *clientData, int mask)
1364 Jim_Obj **objPtrPtr = clientData;
1366 return Jim_EvalObjBackground(interp, *objPtrPtr);
1369 static int aio_eventinfo(Jim_Interp *interp, AioFile * af, unsigned mask, Jim_Obj **scriptHandlerObj,
1370 int argc, Jim_Obj * const *argv)
1372 if (argc == 0) {
1373 /* Return current script */
1374 if (*scriptHandlerObj) {
1375 Jim_SetResult(interp, *scriptHandlerObj);
1377 return JIM_OK;
1380 if (*scriptHandlerObj) {
1381 /* Delete old handler */
1382 Jim_DeleteFileHandler(interp, af->fd, mask);
1385 /* Now possibly add the new script(s) */
1386 if (Jim_Length(argv[0]) == 0) {
1387 /* Empty script, so done */
1388 return JIM_OK;
1391 /* A new script to add */
1392 Jim_IncrRefCount(argv[0]);
1393 *scriptHandlerObj = argv[0];
1395 Jim_CreateFileHandler(interp, af->fd, mask,
1396 JimAioFileEventHandler, scriptHandlerObj, JimAioFileEventFinalizer);
1398 return JIM_OK;
1401 static int aio_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1403 AioFile *af = Jim_CmdPrivData(interp);
1405 return aio_eventinfo(interp, af, JIM_EVENT_READABLE, &af->rEvent, argc, argv);
1408 static int aio_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1410 AioFile *af = Jim_CmdPrivData(interp);
1412 return aio_eventinfo(interp, af, JIM_EVENT_WRITABLE, &af->wEvent, argc, argv);
1415 static int aio_cmd_onexception(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1417 AioFile *af = Jim_CmdPrivData(interp);
1419 return aio_eventinfo(interp, af, JIM_EVENT_EXCEPTION, &af->eEvent, argc, argv);
1421 #endif
1423 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1424 static int aio_cmd_ssl(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1426 AioFile *af = Jim_CmdPrivData(interp);
1427 SSL *ssl;
1428 SSL_CTX *ssl_ctx;
1429 int server = 0;
1430 const char *sni = NULL;
1432 if (argc > 2) {
1433 static const char * const options[] = { "-server", "-sni", NULL };
1434 enum { OPT_SERVER, OPT_SNI };
1435 int option;
1437 if (Jim_GetEnum(interp, argv[2], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
1438 return JIM_ERR;
1440 switch (option) {
1441 case OPT_SERVER:
1442 if (argc != 4 && argc != 5) {
1443 return JIM_ERR;
1445 server = 1;
1446 break;
1448 case OPT_SNI:
1449 if (argc != 4) {
1450 return JIM_ERR;
1452 sni = Jim_String(argv[3]);
1453 break;
1457 if (af->ssl) {
1458 Jim_SetResultFormatted(interp, "%#s: stream is already ssl", argv[0]);
1459 return JIM_ERR;
1462 ssl_ctx = JimAioSslCtx(interp);
1463 if (ssl_ctx == NULL) {
1464 return JIM_ERR;
1467 ssl = SSL_new(ssl_ctx);
1468 if (ssl == NULL) {
1469 goto out;
1472 SSL_set_cipher_list(ssl, "ALL");
1474 if (SSL_set_fd(ssl, fileno(af->fp)) == 0) {
1475 goto out;
1478 if (server) {
1479 const char *certfile = Jim_String(argv[3]);
1480 const char *keyfile = (argc == 4) ? certfile : Jim_String(argv[4]);
1481 if (SSL_use_certificate_file(ssl, certfile, SSL_FILETYPE_PEM) != 1) {
1482 goto out;
1484 if (SSL_use_PrivateKey_file(ssl, keyfile, SSL_FILETYPE_PEM) != 1) {
1485 goto out;
1488 if (SSL_accept(ssl) != 1) {
1489 goto out;
1492 else {
1493 if (sni) {
1494 /* Set server name indication if requested */
1495 SSL_set_tlsext_host_name(ssl, sni);
1497 if (SSL_connect(ssl) != 1) {
1498 goto out;
1502 af->ssl = ssl;
1503 af->fops = &ssl_fops;
1505 /* Set the command name as the result */
1506 Jim_SetResult(interp, argv[0]);
1508 return JIM_OK;
1510 out:
1511 if (ssl) {
1512 SSL_free(ssl);
1514 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1515 return JIM_ERR;
1518 static int aio_cmd_verify(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1520 AioFile *af = Jim_CmdPrivData(interp);
1521 int ret;
1523 if (!af->fops->verify) {
1524 return JIM_OK;
1527 ret = af->fops->verify(af);
1528 if (ret != JIM_OK) {
1529 if (JimCheckStreamError(interp, af) == JIM_OK) {
1530 Jim_SetResultString(interp, "failed to verify the connection authenticity", -1);
1533 return ret;
1535 #endif /* JIM_BOOTSTRAP */
1537 #if defined(HAVE_STRUCT_FLOCK) && !defined(JIM_BOOTSTRAP)
1538 static int aio_cmd_lock(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1540 AioFile *af = Jim_CmdPrivData(interp);
1541 struct flock fl;
1542 int lockmode = F_SETLK;
1544 if (argc == 1) {
1545 if (!Jim_CompareStringImmediate(interp, argv[0], "-wait")) {
1546 return -1;
1548 lockmode = F_SETLKW;
1551 fl.l_start = 0;
1552 fl.l_len = 0;
1553 fl.l_type = F_WRLCK;
1554 fl.l_whence = SEEK_SET;
1556 switch (fcntl(af->fd, lockmode, &fl))
1558 case 0:
1559 Jim_SetResultInt(interp, 1);
1560 break;
1561 case -1:
1562 if (errno == EACCES || errno == EAGAIN)
1563 Jim_SetResultInt(interp, 0);
1564 else
1566 Jim_SetResultFormatted(interp, "lock failed: %s",
1567 strerror(errno));
1568 return JIM_ERR;
1570 break;
1571 default:
1572 Jim_SetResultInt(interp, 0);
1573 break;
1576 return JIM_OK;
1579 static int aio_cmd_unlock(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1581 AioFile *af = Jim_CmdPrivData(interp);
1582 struct flock fl;
1583 fl.l_start = 0;
1584 fl.l_len = 0;
1585 fl.l_type = F_UNLCK;
1586 fl.l_whence = SEEK_SET;
1588 Jim_SetResultInt(interp, fcntl(af->fd, F_SETLK, &fl) == 0);
1589 return JIM_OK;
1591 #endif /* JIM_BOOTSTRAP */
1593 #if defined(HAVE_TERMIOS_H) && !defined(JIM_BOOTSTRAP)
1594 static int aio_cmd_tty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1596 AioFile *af = Jim_CmdPrivData(interp);
1597 Jim_Obj *dictObjPtr;
1598 int ret;
1600 if (argc == 0) {
1601 /* get the current settings as a dictionary */
1602 dictObjPtr = Jim_GetTtySettings(interp, af->fd);
1603 if (dictObjPtr == NULL) {
1604 JimAioSetError(interp, NULL);
1605 return JIM_ERR;
1607 Jim_SetResult(interp, dictObjPtr);
1608 return JIM_OK;
1611 if (argc > 1) {
1612 /* Convert name value arguments to a dictionary */
1613 dictObjPtr = Jim_NewListObj(interp, argv, argc);
1615 else {
1616 /* The settings are already given as a list */
1617 dictObjPtr = argv[0];
1619 Jim_IncrRefCount(dictObjPtr);
1621 if (Jim_ListLength(interp, dictObjPtr) % 2) {
1622 /* Must be a valid dictionary */
1623 Jim_DecrRefCount(interp, dictObjPtr);
1624 return -1;
1627 ret = Jim_SetTtySettings(interp, af->fd, dictObjPtr);
1628 if (ret < 0) {
1629 JimAioSetError(interp, NULL);
1630 ret = JIM_ERR;
1632 Jim_DecrRefCount(interp, dictObjPtr);
1634 return ret;
1636 #endif /* JIM_BOOTSTRAP */
1638 static const jim_subcmd_type aio_command_table[] = {
1639 { "read",
1640 "?-nonewline|-pending|len?",
1641 aio_cmd_read,
1644 /* Description: Read and return bytes from the stream. To eof if no len. */
1646 { "copyto",
1647 "handle ?size?",
1648 aio_cmd_copy,
1651 /* Description: Copy up to 'size' bytes to the given filehandle, or to eof if no size. */
1653 { "getfd",
1654 NULL,
1655 aio_cmd_getfd,
1658 /* Description: Internal command to return the underlying file descriptor. */
1660 { "gets",
1661 "?var?",
1662 aio_cmd_gets,
1665 /* Description: Read one line and return it or store it in the var */
1667 { "puts",
1668 "?-nonewline? str",
1669 aio_cmd_puts,
1672 /* Description: Write the string, with newline unless -nonewline */
1674 { "isatty",
1675 NULL,
1676 aio_cmd_isatty,
1679 /* Description: Is the file descriptor a tty? */
1681 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
1682 { "recvfrom",
1683 "len ?addrvar?",
1684 aio_cmd_recvfrom,
1687 /* Description: Receive up to 'len' bytes on the socket. Sets 'addrvar' with receive address, if set */
1689 { "sendto",
1690 "str address",
1691 aio_cmd_sendto,
1694 /* Description: Send 'str' to the given address (dgram only) */
1696 { "accept",
1697 "?addrvar?",
1698 aio_cmd_accept,
1701 /* Description: Server socket only: Accept a connection and return stream */
1703 { "listen",
1704 "backlog",
1705 aio_cmd_listen,
1708 /* Description: Set the listen backlog for server socket */
1710 { "sockopt",
1711 "?opt 0|1?",
1712 aio_cmd_sockopt,
1715 /* Description: Return a dictionary of sockopts, or set the value of a sockopt */
1717 { "sockname",
1718 NULL,
1719 aio_cmd_sockname,
1722 /* Description: Returns the local address of the socket, if any */
1724 { "peername",
1725 NULL,
1726 aio_cmd_peername,
1729 /* Description: Returns the remote address of the socket, if any */
1731 #endif /* JIM_BOOTSTRAP */
1732 { "flush",
1733 NULL,
1734 aio_cmd_flush,
1737 /* Description: Flush the stream */
1739 { "eof",
1740 NULL,
1741 aio_cmd_eof,
1744 /* Description: Returns 1 if stream is at eof */
1746 { "close",
1747 "?r(ead)|w(rite)?",
1748 aio_cmd_close,
1751 JIM_MODFLAG_FULLARGV,
1752 /* Description: Closes the stream. */
1754 { "seek",
1755 "offset ?start|current|end",
1756 aio_cmd_seek,
1759 /* Description: Seeks in the stream (default 'current') */
1761 { "tell",
1762 NULL,
1763 aio_cmd_tell,
1766 /* Description: Returns the current seek position */
1768 { "filename",
1769 NULL,
1770 aio_cmd_filename,
1773 /* Description: Returns the original filename */
1775 #ifdef O_NDELAY
1776 { "ndelay",
1777 "?0|1?",
1778 aio_cmd_ndelay,
1781 /* Description: Set O_NDELAY (if arg). Returns current/new setting. */
1783 #endif
1784 #ifdef HAVE_FSYNC
1785 { "sync",
1786 NULL,
1787 aio_cmd_sync,
1790 /* Description: Flush and fsync() the stream */
1792 #endif
1793 { "buffering",
1794 "none|line|full",
1795 aio_cmd_buffering,
1798 /* Description: Sets buffering */
1800 #ifdef jim_ext_eventloop
1801 { "readable",
1802 "?readable-script?",
1803 aio_cmd_readable,
1806 /* Description: Returns script, or invoke readable-script when readable, {} to remove */
1808 { "writable",
1809 "?writable-script?",
1810 aio_cmd_writable,
1813 /* Description: Returns script, or invoke writable-script when writable, {} to remove */
1815 { "onexception",
1816 "?exception-script?",
1817 aio_cmd_onexception,
1820 /* Description: Returns script, or invoke exception-script when oob data, {} to remove */
1822 #endif
1823 #if !defined(JIM_BOOTSTRAP)
1824 #if defined(JIM_SSL)
1825 { "ssl",
1826 "?-server cert ?priv?|-sni servername?",
1827 aio_cmd_ssl,
1830 JIM_MODFLAG_FULLARGV
1831 /* Description: Wraps a stream socket with SSL/TLS and returns a new channel */
1833 { "verify",
1834 NULL,
1835 aio_cmd_verify,
1838 /* Description: Verifies the certificate of a SSL/TLS channel */
1840 #endif
1841 #if defined(HAVE_STRUCT_FLOCK)
1842 { "lock ?-wait?",
1843 NULL,
1844 aio_cmd_lock,
1847 /* Description: Attempt to get a lock, possibly waiting */
1849 { "unlock",
1850 NULL,
1851 aio_cmd_unlock,
1854 /* Description: Relase a lock. */
1856 #endif
1857 #if defined(HAVE_TERMIOS_H)
1858 { "tty",
1859 "?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?",
1860 aio_cmd_tty,
1863 /* Description: Get or set tty settings - valid only on a tty */
1865 #endif
1866 #endif /* JIM_BOOTSTRAP */
1867 { NULL }
1870 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1872 return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, aio_command_table, argc, argv), argc, argv);
1875 static int JimAioOpenCommand(Jim_Interp *interp, int argc,
1876 Jim_Obj *const *argv)
1878 const char *mode;
1880 if (argc != 2 && argc != 3) {
1881 Jim_WrongNumArgs(interp, 1, argv, "filename ?mode?");
1882 return JIM_ERR;
1885 mode = (argc == 3) ? Jim_String(argv[2]) : "r";
1887 #ifdef jim_ext_tclcompat
1889 const char *filename = Jim_String(argv[1]);
1891 /* If the filename starts with '|', use popen instead */
1892 if (*filename == '|') {
1893 Jim_Obj *evalObj[3];
1895 evalObj[0] = Jim_NewStringObj(interp, "::popen", -1);
1896 evalObj[1] = Jim_NewStringObj(interp, filename + 1, -1);
1897 evalObj[2] = Jim_NewStringObj(interp, mode, -1);
1899 return Jim_EvalObjVector(interp, 3, evalObj);
1902 #endif
1903 return JimMakeChannel(interp, NULL, -1, argv[1], "aio.handle%ld", 0, mode, 0) ? JIM_OK : JIM_ERR;
1906 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1907 static void JimAioSslContextDelProc(struct Jim_Interp *interp, void *privData)
1909 SSL_CTX_free((SSL_CTX *)privData);
1910 ERR_free_strings();
1913 #ifdef USE_TLSv1_2_method
1914 #define TLS_method TLSv1_2_method
1915 #endif
1917 static SSL_CTX *JimAioSslCtx(Jim_Interp *interp)
1919 SSL_CTX *ssl_ctx = (SSL_CTX *)Jim_GetAssocData(interp, "ssl_ctx");
1920 if (ssl_ctx == NULL) {
1921 SSL_load_error_strings();
1922 SSL_library_init();
1923 ssl_ctx = SSL_CTX_new(TLS_method());
1924 if (ssl_ctx && SSL_CTX_set_default_verify_paths(ssl_ctx)) {
1925 SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
1926 Jim_SetAssocData(interp, "ssl_ctx", JimAioSslContextDelProc, ssl_ctx);
1927 } else {
1928 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1931 return ssl_ctx;
1933 #endif /* JIM_BOOTSTRAP */
1936 * Creates a channel for fh/fd/filename.
1938 * If fh is not NULL, uses that as the channel (and sets AIO_KEEPOPEN).
1939 * Otherwise, if fd is >= 0, uses that as the channel.
1940 * Otherwise opens 'filename' with mode 'mode'.
1942 * hdlfmt is a sprintf format for the filehandle. Anything with %ld at the end will do.
1943 * mode is used for open or fdopen.
1945 * Creates the command and sets the name as the current result.
1946 * Returns the AioFile pointer on sucess or NULL on failure.
1948 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
1949 const char *hdlfmt, int family, const char *mode, int flags)
1951 AioFile *af;
1952 char buf[AIO_CMD_LEN];
1954 snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
1955 if (!filename) {
1956 filename = Jim_NewStringObj(interp, buf, -1);
1959 Jim_IncrRefCount(filename);
1961 if (fh == NULL) {
1962 if (fd >= 0) {
1963 #ifndef JIM_ANSIC
1964 fh = fdopen(fd, mode);
1965 #endif
1967 else
1968 fh = fopen(Jim_String(filename), mode);
1970 if (fh == NULL) {
1971 JimAioSetError(interp, filename);
1972 #ifndef JIM_ANSIC
1973 if (fd >= 0) {
1974 close(fd);
1976 #endif
1977 Jim_DecrRefCount(interp, filename);
1978 return NULL;
1982 /* Create the file command */
1983 af = Jim_Alloc(sizeof(*af));
1984 memset(af, 0, sizeof(*af));
1985 af->fp = fh;
1986 af->filename = filename;
1987 af->flags = flags;
1988 #ifndef JIM_ANSIC
1989 af->fd = fileno(fh);
1990 #ifdef FD_CLOEXEC
1991 if ((flags & AIO_KEEPOPEN) == 0) {
1992 (void)fcntl(af->fd, F_SETFD, FD_CLOEXEC);
1994 #endif
1995 #endif
1996 af->addr_family = family;
1997 af->fops = &stdio_fops;
1998 af->ssl = NULL;
2000 Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc);
2002 /* Note that the command must use the global namespace, even if
2003 * the current namespace is something different
2005 Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, Jim_NewStringObj(interp, buf, -1)));
2007 return af;
2010 #if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && UNIX_SOCKETS) || defined(HAVE_OPENPTY)
2012 * Create a pair of channels. e.g. from pipe() or socketpair()
2014 static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename,
2015 const char *hdlfmt, int family, const char * const mode[2])
2017 if (JimMakeChannel(interp, NULL, p[0], filename, hdlfmt, family, mode[0], 0)) {
2018 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
2019 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
2020 if (JimMakeChannel(interp, NULL, p[1], filename, hdlfmt, family, mode[1], 0)) {
2021 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
2022 Jim_SetResult(interp, objPtr);
2023 return JIM_OK;
2027 /* Can only be here if fdopen() failed */
2028 close(p[0]);
2029 close(p[1]);
2030 JimAioSetError(interp, NULL);
2031 return JIM_ERR;
2033 #endif
2035 #ifdef HAVE_PIPE
2036 static int JimAioPipeCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2038 int p[2];
2039 static const char * const mode[2] = { "r", "w" };
2041 if (argc != 1) {
2042 Jim_WrongNumArgs(interp, 1, argv, "");
2043 return JIM_ERR;
2046 if (pipe(p) != 0) {
2047 JimAioSetError(interp, NULL);
2048 return JIM_ERR;
2051 return JimMakeChannelPair(interp, p, argv[0], "aio.pipe%ld", 0, mode);
2053 #endif
2055 #ifdef HAVE_OPENPTY
2056 static int JimAioOpenPtyCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2058 int p[2];
2059 static const char * const mode[2] = { "r+", "w+" };
2061 if (argc != 1) {
2062 Jim_WrongNumArgs(interp, 1, argv, "");
2063 return JIM_ERR;
2066 if (openpty(&p[0], &p[1], NULL, NULL, NULL) != 0) {
2067 JimAioSetError(interp, NULL);
2068 return JIM_ERR;
2071 return JimMakeChannelPair(interp, p, argv[0], "aio.pty%ld", 0, mode);
2073 #endif
2075 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
2077 static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2079 const char *socktypes[] = {
2080 "unix",
2081 "unix.server",
2082 "unix.dgram",
2083 "unix.dgram.server",
2084 "dgram",
2085 "dgram.server",
2086 "stream",
2087 "stream.server",
2088 "pipe",
2089 "pair",
2090 "pty",
2091 NULL
2093 enum
2095 SOCK_UNIX,
2096 SOCK_UNIX_SERVER,
2097 SOCK_UNIX_DGRAM,
2098 SOCK_UNIX_DGRAM_SERVER,
2099 SOCK_DGRAM_CLIENT,
2100 SOCK_DGRAM_SERVER,
2101 SOCK_STREAM_CLIENT,
2102 SOCK_STREAM_SERVER,
2103 SOCK_STREAM_PIPE,
2104 SOCK_STREAM_SOCKETPAIR,
2105 SOCK_STREAM_PTY,
2107 int socktype;
2108 int sock;
2109 const char *addr = NULL;
2110 const char *bind_addr = NULL;
2111 const char *connect_addr = NULL;
2112 union sockaddr_any sa;
2113 socklen_t salen;
2114 int on = 1;
2115 int reuse = 0;
2116 int do_listen = 0;
2117 int family = PF_INET;
2118 int type = SOCK_STREAM;
2119 Jim_Obj *argv0 = argv[0];
2120 int ipv6 = 0;
2122 if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-ipv6")) {
2123 if (!IPV6) {
2124 Jim_SetResultString(interp, "ipv6 not supported", -1);
2125 return JIM_ERR;
2127 ipv6 = 1;
2128 family = PF_INET6;
2130 argc -= ipv6;
2131 argv += ipv6;
2133 if (argc < 2) {
2134 wrongargs:
2135 Jim_WrongNumArgs(interp, 1, &argv0, "?-ipv6? type ?address?");
2136 return JIM_ERR;
2139 if (Jim_GetEnum(interp, argv[1], socktypes, &socktype, "socket type", JIM_ERRMSG) != JIM_OK)
2140 return Jim_CheckShowCommands(interp, argv[1], socktypes);
2142 Jim_SetEmptyResult(interp);
2144 if (argc > 2) {
2145 addr = Jim_String(argv[2]);
2148 #if defined(HAVE_SOCKETPAIR) && UNIX_SOCKETS
2149 if (socktype == SOCK_STREAM_SOCKETPAIR) {
2150 int p[2];
2151 static const char * const mode[2] = { "r+", "r+" };
2153 if (addr || ipv6) {
2154 goto wrongargs;
2157 if (socketpair(PF_UNIX, SOCK_STREAM, 0, p) < 0) {
2158 JimAioSetError(interp, NULL);
2159 return JIM_ERR;
2161 return JimMakeChannelPair(interp, p, argv[1], "aio.sockpair%ld", PF_UNIX, mode);
2163 #endif
2165 #if defined(HAVE_PIPE)
2166 if (socktype == SOCK_STREAM_PIPE) {
2167 if (addr || ipv6) {
2168 goto wrongargs;
2170 return JimAioPipeCommand(interp, 1, &argv[1]);
2172 #endif
2174 /* Now all these socket types are very similar */
2175 switch (socktype) {
2176 case SOCK_DGRAM_CLIENT:
2177 connect_addr = addr;
2178 type = SOCK_DGRAM;
2179 break;
2181 case SOCK_STREAM_CLIENT:
2182 if (addr == NULL) {
2183 goto wrongargs;
2185 connect_addr = addr;
2186 break;
2188 case SOCK_STREAM_SERVER:
2189 if (addr == NULL) {
2190 goto wrongargs;
2192 bind_addr = addr;
2193 reuse = 1;
2194 do_listen = 1;
2195 break;
2197 case SOCK_DGRAM_SERVER:
2198 if (addr == NULL) {
2199 goto wrongargs;
2201 bind_addr = addr;
2202 type = SOCK_DGRAM;
2203 reuse = 1;
2204 break;
2206 #if UNIX_SOCKETS
2207 case SOCK_UNIX:
2208 if (addr == NULL) {
2209 goto wrongargs;
2211 connect_addr = addr;
2212 family = PF_UNIX;
2213 break;
2215 case SOCK_UNIX_DGRAM:
2216 connect_addr = addr;
2217 type = SOCK_DGRAM;
2218 family = PF_UNIX;
2219 /* A dgram unix domain socket client needs to bind
2220 * to a temporary address to allow the server to
2221 * send responses
2224 int tmpfd = Jim_MakeTempFile(interp, NULL, 1);
2225 if (tmpfd < 0) {
2226 return JIM_ERR;
2228 close(tmpfd);
2229 /* This will be valid until a result is next set, which is long enough here */
2230 bind_addr = Jim_String(Jim_GetResult(interp));
2232 break;
2234 case SOCK_UNIX_SERVER:
2235 if (addr == NULL) {
2236 goto wrongargs;
2238 bind_addr = addr;
2239 family = PF_UNIX;
2240 do_listen = 1;
2241 break;
2243 case SOCK_UNIX_DGRAM_SERVER:
2244 if (addr == NULL) {
2245 goto wrongargs;
2247 bind_addr = addr;
2248 type = SOCK_DGRAM;
2249 family = PF_UNIX;
2250 break;
2251 #endif
2252 #ifdef HAVE_OPENPTY
2253 case SOCK_STREAM_PTY:
2254 if (addr || ipv6) {
2255 goto wrongargs;
2257 return JimAioOpenPtyCommand(interp, 1, &argv[1]);
2258 #endif
2260 default:
2261 Jim_SetResultString(interp, "Unsupported socket type", -1);
2262 return JIM_ERR;
2265 /* Now do all the steps necessary for the given socket type */
2266 sock = socket(family, type, 0);
2267 if (sock < 0) {
2268 JimAioSetError(interp, NULL);
2269 return JIM_ERR;
2271 if (bind_addr) {
2272 if (JimParseSocketAddress(interp, family, bind_addr, &sa, &salen) != JIM_OK) {
2273 close(sock);
2274 return JIM_ERR;
2276 if (reuse) {
2277 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
2279 if (bind(sock, &sa.sa, salen)) {
2280 Jim_SetResultFormatted(interp, "%s: bind: %s", bind_addr, strerror(errno));
2281 close(sock);
2282 return JIM_ERR;
2285 if (connect_addr) {
2286 if (JimParseSocketAddress(interp, family, connect_addr, &sa, &salen) != JIM_OK) {
2287 close(sock);
2288 return JIM_ERR;
2290 if (connect(sock, &sa.sa, salen)) {
2291 Jim_SetResultFormatted(interp, "%s: connect: %s", connect_addr, strerror(errno));
2292 close(sock);
2293 return JIM_ERR;
2296 if (do_listen) {
2297 if (listen(sock, 5)) {
2298 Jim_SetResultFormatted(interp, "listen: %s", strerror(errno));
2299 close(sock);
2300 return JIM_ERR;
2304 return JimMakeChannel(interp, NULL, sock, argv[1], "aio.sock%ld", family, "r+", 0) ? JIM_OK : JIM_ERR;
2306 #endif /* JIM_BOOTSTRAP */
2308 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
2309 static int JimAioLoadSSLCertsCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2311 SSL_CTX *ssl_ctx;
2313 if (argc != 2) {
2314 Jim_WrongNumArgs(interp, 1, argv, "dir");
2315 return JIM_ERR;
2318 ssl_ctx = JimAioSslCtx(interp);
2319 if (!ssl_ctx) {
2320 return JIM_ERR;
2322 if (SSL_CTX_load_verify_locations(ssl_ctx, NULL, Jim_String(argv[1])) == 1) {
2323 return JIM_OK;
2325 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
2326 return JIM_ERR;
2328 #endif /* JIM_BOOTSTRAP */
2330 int Jim_aioInit(Jim_Interp *interp)
2332 if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG))
2333 return JIM_ERR;
2335 #if defined(JIM_SSL)
2336 Jim_CreateCommand(interp, "load_ssl_certs", JimAioLoadSSLCertsCommand, NULL, NULL);
2337 #endif
2339 Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL);
2340 #ifdef HAVE_SOCKETS
2341 Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL);
2342 #endif
2343 #ifdef HAVE_PIPE
2344 Jim_CreateCommand(interp, "pipe", JimAioPipeCommand, NULL, NULL);
2345 #endif
2347 /* Create filehandles for stdin, stdout and stderr */
2348 JimMakeChannel(interp, stdin, -1, NULL, "stdin", 0, "r", AIO_KEEPOPEN);
2349 JimMakeChannel(interp, stdout, -1, NULL, "stdout", 0, "w", AIO_KEEPOPEN);
2350 JimMakeChannel(interp, stderr, -1, NULL, "stderr", 0, "w", AIO_KEEPOPEN);
2352 return JIM_OK;