docs: fix minor typos
[jimtcl.git] / jim-aio.c
blobddfc486801064a84e23e43a3d02296a38d77cd4d
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
55 #include "jim.h"
56 #include "jimiocompat.h"
58 #if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H)
59 #include <sys/socket.h>
60 #include <netinet/in.h>
61 #include <netinet/tcp.h>
62 #include <arpa/inet.h>
63 #include <netdb.h>
64 #ifdef HAVE_SYS_UN_H
65 #include <sys/un.h>
66 #endif
67 #define HAVE_SOCKETS
68 #elif defined (__MINGW32__)
69 /* currently mingw32 doesn't support sockets, but has pipe, fdopen */
70 #else
71 #define JIM_ANSIC
72 #endif
74 #if defined(JIM_SSL)
75 #include <openssl/ssl.h>
76 #include <openssl/err.h>
77 #endif
79 #ifdef HAVE_TERMIOS_H
80 #include <jim-tty.h>
81 #endif
83 #include "jim-eventloop.h"
84 #include "jim-subcmd.h"
86 #define AIO_CMD_LEN 32 /* e.g. aio.handleXXXXXX */
87 #define AIO_BUF_LEN 256 /* Can keep this small and rely on stdio buffering */
89 #ifndef HAVE_FTELLO
90 #define ftello ftell
91 #endif
92 #ifndef HAVE_FSEEKO
93 #define fseeko fseek
94 #endif
96 #define AIO_KEEPOPEN 1
97 #define AIO_NODELETE 2
99 #if defined(JIM_IPV6)
100 #define IPV6 1
101 #else
102 #define IPV6 0
103 #ifndef PF_INET6
104 #define PF_INET6 0
105 #endif
106 #endif
107 #if defined(HAVE_SYS_UN_H) && defined(PF_UNIX)
108 #define UNIX_SOCKETS 1
109 #else
110 #define UNIX_SOCKETS 0
111 #endif
113 #ifdef JIM_ANSIC
114 /* no fdopen() with ANSIC, so can't support these */
115 #undef HAVE_PIPE
116 #undef HAVE_SOCKETPAIR
117 #endif
119 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
120 /* Avoid type punned pointers */
121 union sockaddr_any {
122 struct sockaddr sa;
123 struct sockaddr_in sin;
124 #if IPV6
125 struct sockaddr_in6 sin6;
126 #endif
127 #if UNIX_SOCKETS
128 struct sockaddr_un sun;
129 #endif
132 #ifndef HAVE_INET_NTOP
133 const char *inet_ntop(int af, const void *src, char *dst, int size)
135 if (af != PF_INET) {
136 return NULL;
138 snprintf(dst, size, "%s", inet_ntoa(((struct sockaddr_in *)src)->sin_addr));
139 return dst;
141 #endif
142 #endif /* JIM_BOOTSTRAP */
144 struct AioFile;
146 typedef struct {
147 int (*writer)(struct AioFile *af, const char *buf, int len);
148 int (*reader)(struct AioFile *af, char *buf, int len);
149 const char *(*getline)(struct AioFile *af, char *buf, int len);
150 int (*error)(const struct AioFile *af);
151 const char *(*strerror)(struct AioFile *af);
152 int (*verify)(struct AioFile *af);
153 } JimAioFopsType;
155 typedef struct AioFile
157 FILE *fp;
158 Jim_Obj *filename;
159 int type;
160 int openFlags; /* AIO_KEEPOPEN? keep FILE* */
161 int fd;
162 Jim_Obj *rEvent;
163 Jim_Obj *wEvent;
164 Jim_Obj *eEvent;
165 int addr_family;
166 void *ssl;
167 const JimAioFopsType *fops;
168 } AioFile;
170 static int stdio_writer(struct AioFile *af, const char *buf, int len)
172 return fwrite(buf, 1, len, af->fp);
175 static int stdio_reader(struct AioFile *af, char *buf, int len)
177 return fread(buf, 1, len, af->fp);
180 static const char *stdio_getline(struct AioFile *af, char *buf, int len)
182 return fgets(buf, len, af->fp);
185 static int stdio_error(const AioFile *af)
187 if (!ferror(af->fp)) {
188 return JIM_OK;
190 clearerr(af->fp);
191 /* EAGAIN and similar are not error conditions. Just treat them like eof */
192 if (feof(af->fp) || errno == EAGAIN || errno == EINTR) {
193 return JIM_OK;
195 #ifdef ECONNRESET
196 if (errno == ECONNRESET) {
197 return JIM_OK;
199 #endif
200 #ifdef ECONNABORTED
201 if (errno == ECONNABORTED) {
202 return JIM_OK;
204 #endif
205 return JIM_ERR;
208 static const char *stdio_strerror(struct AioFile *af)
210 return strerror(errno);
213 static const JimAioFopsType stdio_fops = {
214 stdio_writer,
215 stdio_reader,
216 stdio_getline,
217 stdio_error,
218 stdio_strerror,
219 NULL
222 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
224 static SSL_CTX *JimAioSslCtx(Jim_Interp *interp);
226 static int ssl_writer(struct AioFile *af, const char *buf, int len)
228 return SSL_write(af->ssl, buf, len);
231 static int ssl_reader(struct AioFile *af, char *buf, int len)
233 return SSL_read(af->ssl, buf, len);
236 static const char *ssl_getline(struct AioFile *af, char *buf, int len)
238 size_t i;
239 for (i = 0; i < len + 1; i++) {
240 if (SSL_read(af->ssl, &buf[i], 1) != 1) {
241 if (i == 0) {
242 return NULL;
244 break;
246 if (buf[i] == '\n') {
247 break;
250 buf[i] = '\0';
251 return buf;
254 static int ssl_error(const struct AioFile *af)
256 if (ERR_peek_error() == 0) {
257 return JIM_OK;
260 return JIM_ERR;
263 static const char *ssl_strerror(struct AioFile *af)
265 int err = ERR_get_error();
267 if (err) {
268 return ERR_error_string(err, NULL);
270 else {
271 return stdio_strerror(af);
275 static int ssl_verify(struct AioFile *af)
277 X509 *cert;
279 cert = SSL_get_peer_certificate(af->ssl);
280 if (!cert) {
281 return JIM_ERR;
283 X509_free(cert);
285 if (SSL_get_verify_result(af->ssl) == X509_V_OK) {
286 return JIM_OK;
289 return JIM_ERR;
292 static const JimAioFopsType ssl_fops = {
293 ssl_writer,
294 ssl_reader,
295 ssl_getline,
296 ssl_error,
297 ssl_strerror,
298 ssl_verify
300 #endif /* JIM_BOOTSTRAP */
302 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
303 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
304 const char *hdlfmt, int family, const char *mode, int flags);
306 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
307 #ifndef HAVE_GETADDRINFO
309 * Poor man's getaddrinfo().
310 * hints->ai_family must be set and must be PF_INET or PF_INET6
311 * Only returns the first matching result.
312 * servname must be numeric.
314 struct addrinfo {
315 int ai_family;
316 socklen_t ai_addrlen;
317 struct sockaddr *ai_addr; /* simply points to ai_storage */
318 union sockaddr_any ai_storage;
321 static int getaddrinfo(const char *hostname, const char *servname,
322 const struct addrinfo *hints, struct addrinfo **res)
324 struct hostent *he;
325 char *end;
326 unsigned long port = strtoul(servname, &end, 10);
327 if (port == 0 || port > 65536 || *end) {
328 errno = EINVAL;
329 return -1;
332 if ((he = gethostbyname(hostname)) != NULL) {
333 int i;
334 for (i = 0; he->h_addr_list[i]; i++) {
335 if (he->h_addrtype == hints->ai_family) {
336 struct addrinfo *ai = malloc(sizeof(*ai));
337 memset(ai, 0, sizeof(*ai));
338 ai->ai_family = he->h_addrtype;
339 ai->ai_addr = &ai->ai_storage.sa;
340 if (ai->ai_family == PF_INET) {
341 ai->ai_addrlen = sizeof(ai->ai_storage.sin);
342 ai->ai_storage.sin.sin_family = he->h_addrtype;
343 assert(sizeof(ai->ai_storage.sin.sin_addr) == he->h_length);
344 memcpy(&ai->ai_storage.sin.sin_addr, he->h_addr_list[i], he->h_length);
345 ai->ai_storage.sin.sin_port = htons(port);
347 #if IPV6
348 else {
349 ai->ai_addrlen = sizeof(ai->ai_storage.sin6);
350 ai->ai_storage.sin6.sin6_family = he->h_addrtype;
351 assert(sizeof(ai->ai_storage.sin6.sin6_addr) == he->h_length);
352 memcpy(&ai->ai_storage.sin6.sin6_addr, he->h_addr_list[i], he->h_length);
353 ai->ai_storage.sin6.sin6_port = htons(port);
355 #endif
356 *res = ai;
357 return 0;
361 return ENOENT;
364 static void freeaddrinfo(struct addrinfo *ai)
366 free(ai);
368 #endif
370 static int JimParseIPv6Address(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, socklen_t *salen)
372 #if IPV6
374 * An IPv6 addr/port looks like:
375 * [::1]
376 * [::1]:2000
377 * [fe80::223:6cff:fe95:bdc0%en1]:2000
378 * [::]:2000
379 * 2000
381 * Note that the "any" address is ::, which is the same as when no address is specified.
383 char *sthost = NULL;
384 const char *stport;
385 int ret = JIM_OK;
386 struct addrinfo req;
387 struct addrinfo *ai;
389 stport = strrchr(hostport, ':');
390 if (!stport) {
391 /* No : so, the whole thing is the port */
392 stport = hostport;
393 hostport = "::";
394 sthost = Jim_StrDup(hostport);
396 else {
397 stport++;
400 if (*hostport == '[') {
401 /* This is a numeric ipv6 address */
402 char *pt = strchr(++hostport, ']');
403 if (pt) {
404 sthost = Jim_StrDupLen(hostport, pt - hostport);
408 if (!sthost) {
409 sthost = Jim_StrDupLen(hostport, stport - hostport - 1);
412 memset(&req, '\0', sizeof(req));
413 req.ai_family = PF_INET6;
415 if (getaddrinfo(sthost, stport, &req, &ai)) {
416 Jim_SetResultFormatted(interp, "Not a valid address: %s", hostport);
417 ret = JIM_ERR;
419 else {
420 memcpy(&sa->sin6, ai->ai_addr, ai->ai_addrlen);
421 *salen = ai->ai_addrlen;
422 freeaddrinfo(ai);
424 Jim_Free(sthost);
426 return ret;
427 #else
428 Jim_SetResultString(interp, "ipv6 not supported", -1);
429 return JIM_ERR;
430 #endif
433 static int JimParseIpAddress(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, socklen_t *salen)
435 /* An IPv4 addr/port looks like:
436 * 192.168.1.5
437 * 192.168.1.5:2000
438 * 2000
440 * If the address is missing, INADDR_ANY is used.
441 * If the port is missing, 0 is used (only useful for server sockets).
443 char *sthost = NULL;
444 const char *stport;
445 int ret = JIM_OK;
446 struct addrinfo req;
447 struct addrinfo *ai;
449 stport = strrchr(hostport, ':');
450 if (!stport) {
451 /* No : so, the whole thing is the port */
452 stport = hostport;
453 sthost = Jim_StrDup("0.0.0.0");
455 else {
456 sthost = Jim_StrDupLen(hostport, stport - hostport);
457 stport++;
460 memset(&req, '\0', sizeof(req));
461 req.ai_family = PF_INET;
463 if (getaddrinfo(sthost, stport, &req, &ai)) {
464 ret = JIM_ERR;
466 else {
467 memcpy(&sa->sin, ai->ai_addr, ai->ai_addrlen);
468 *salen = ai->ai_addrlen;
469 freeaddrinfo(ai);
471 Jim_Free(sthost);
473 if (ret != JIM_OK) {
474 Jim_SetResultFormatted(interp, "Not a valid address: %s", hostport);
477 return ret;
480 #if UNIX_SOCKETS
481 static int JimParseDomainAddress(Jim_Interp *interp, const char *path, union sockaddr_any *sa, socklen_t *salen)
483 sa->sun.sun_family = PF_UNIX;
484 snprintf(sa->sun.sun_path, sizeof(sa->sun.sun_path), "%s", path);
485 *salen = strlen(sa->sun.sun_path) + 1 + sizeof(sa->sun.sun_family);
487 return JIM_OK;
489 #endif
491 static int JimParseSocketAddress(Jim_Interp *interp, int family, const char *addr, union sockaddr_any *sa, socklen_t *salen)
493 switch (family) {
494 #if UNIX_SOCKETS
495 case PF_UNIX:
496 return JimParseDomainAddress(interp, addr, sa, salen);
497 #endif
498 case PF_INET6:
499 return JimParseIPv6Address(interp, addr, sa, salen);
500 case PF_INET:
501 return JimParseIpAddress(interp, addr, sa, salen);
503 return JIM_ERR;
507 * Format that address in 'sa' as a string and return it as a zero-refcount object.
510 static Jim_Obj *JimFormatSocketAddress(Jim_Interp *interp, const union sockaddr_any *sa, socklen_t salen)
512 /* INET6_ADDRSTRLEN is 46. Add some for [] and port */
513 char addrbuf[60];
514 const char *addr = addrbuf;
515 int addrlen = -1;
517 switch (sa->sa.sa_family) {
518 #if UNIX_SOCKETS
519 case PF_UNIX:
520 addr = sa->sun.sun_path;
521 addrlen = salen - 1 - sizeof(sa->sun.sun_family);
522 if (addrlen < 0) {
523 addrlen = 0;
525 break;
526 #endif
527 #if IPV6
528 case PF_INET6:
529 addrbuf[0] = '[';
530 /* Allow 9 for []:65535\0 */
531 inet_ntop(sa->sa.sa_family, &sa->sin6.sin6_addr, addrbuf + 1, sizeof(addrbuf) - 9);
532 snprintf(addrbuf + strlen(addrbuf), 8, "]:%d", ntohs(sa->sin6.sin6_port));
533 break;
534 #endif
535 case PF_INET:
536 /* Allow 7 for :65535\0 */
537 inet_ntop(sa->sa.sa_family, &sa->sin.sin_addr, addrbuf, sizeof(addrbuf) - 7);
538 snprintf(addrbuf + strlen(addrbuf), 7, ":%d", ntohs(sa->sin.sin_port));
539 break;
541 default:
542 /* Otherwise just an empty address */
543 addr = "";
544 fprintf(stderr, "%s:%d", __FILE__, __LINE__);
545 break;
548 return Jim_NewStringObj(interp, addr, addrlen);
551 static int JimSetVariableSocketAddress(Jim_Interp *interp, Jim_Obj *varObjPtr, const union sockaddr_any *sa, socklen_t salen)
553 int ret;
554 Jim_Obj *objPtr = JimFormatSocketAddress(interp, sa, salen);
555 Jim_IncrRefCount(objPtr);
556 ret = Jim_SetVariable(interp, varObjPtr, objPtr);
557 Jim_DecrRefCount(interp, objPtr);
558 return ret;
561 static Jim_Obj *aio_sockname(Jim_Interp *interp, AioFile *af)
563 union sockaddr_any sa;
564 socklen_t salen = sizeof(sa);
566 if (getsockname(af->fd, &sa.sa, &salen) < 0) {
567 return NULL;
569 return JimFormatSocketAddress(interp, &sa, salen);
571 #endif /* JIM_BOOTSTRAP */
573 static const char *JimAioErrorString(AioFile *af)
575 if (af && af->fops)
576 return af->fops->strerror(af);
578 return strerror(errno);
581 static void JimAioSetError(Jim_Interp *interp, Jim_Obj *name)
583 AioFile *af = Jim_CmdPrivData(interp);
585 if (name) {
586 Jim_SetResultFormatted(interp, "%#s: %s", name, JimAioErrorString(af));
588 else {
589 Jim_SetResultString(interp, JimAioErrorString(af), -1);
593 static int JimCheckStreamError(Jim_Interp *interp, AioFile *af)
595 int ret = af->fops->error(af);
596 if (ret) {
597 JimAioSetError(interp, af->filename);
599 return ret;
602 static void JimAioDelProc(Jim_Interp *interp, void *privData)
604 AioFile *af = privData;
606 JIM_NOTUSED(interp);
608 #if UNIX_SOCKETS
609 if (af->addr_family == PF_UNIX && (af->openFlags & AIO_NODELETE) == 0) {
610 /* If this is bound, delete the socket file now */
611 Jim_Obj *filenameObj = aio_sockname(interp, af);
612 if (filenameObj) {
613 if (Jim_Length(filenameObj)) {
614 remove(Jim_String(filenameObj));
616 Jim_FreeNewObj(interp, filenameObj);
619 #endif
621 Jim_DecrRefCount(interp, af->filename);
623 #ifdef jim_ext_eventloop
624 /* remove all existing EventHandlers */
625 Jim_DeleteFileHandler(interp, af->fd, JIM_EVENT_READABLE | JIM_EVENT_WRITABLE | JIM_EVENT_EXCEPTION);
626 #endif
628 #if defined(JIM_SSL)
629 if (af->ssl != NULL) {
630 SSL_free(af->ssl);
632 #endif
633 if (!(af->openFlags & AIO_KEEPOPEN)) {
634 fclose(af->fp);
637 Jim_Free(af);
640 static int aio_cmd_read(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
642 AioFile *af = Jim_CmdPrivData(interp);
643 char buf[AIO_BUF_LEN];
644 Jim_Obj *objPtr;
645 int nonewline = 0;
646 jim_wide neededLen = -1; /* -1 is "read as much as possible" */
648 if (argc && Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
649 nonewline = 1;
650 argv++;
651 argc--;
653 if (argc == 1) {
654 if (Jim_GetWide(interp, argv[0], &neededLen) != JIM_OK)
655 return JIM_ERR;
656 if (neededLen < 0) {
657 Jim_SetResultString(interp, "invalid parameter: negative len", -1);
658 return JIM_ERR;
661 else if (argc) {
662 return -1;
664 objPtr = Jim_NewStringObj(interp, NULL, 0);
665 while (neededLen != 0) {
666 int retval;
667 int readlen;
669 if (neededLen == -1) {
670 readlen = AIO_BUF_LEN;
672 else {
673 readlen = (neededLen > AIO_BUF_LEN ? AIO_BUF_LEN : neededLen);
675 retval = af->fops->reader(af, buf, readlen);
676 if (retval > 0) {
677 Jim_AppendString(interp, objPtr, buf, retval);
678 if (neededLen != -1) {
679 neededLen -= retval;
682 if (retval != readlen)
683 break;
685 /* Check for error conditions */
686 if (JimCheckStreamError(interp, af)) {
687 Jim_FreeNewObj(interp, objPtr);
688 return JIM_ERR;
690 if (nonewline) {
691 int len;
692 const char *s = Jim_GetString(objPtr, &len);
694 if (len > 0 && s[len - 1] == '\n') {
695 objPtr->length--;
696 objPtr->bytes[objPtr->length] = '\0';
699 Jim_SetResult(interp, objPtr);
700 return JIM_OK;
703 AioFile *Jim_AioFile(Jim_Interp *interp, Jim_Obj *command)
705 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, command, JIM_ERRMSG);
707 /* XXX: There ought to be a supported API for this */
708 if (cmdPtr && !cmdPtr->isproc && cmdPtr->u.native.cmdProc == JimAioSubCmdProc) {
709 return (AioFile *) cmdPtr->u.native.privData;
711 Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", command);
712 return NULL;
715 FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command)
717 AioFile *af;
719 af = Jim_AioFile(interp, command);
720 if (af == NULL) {
721 return NULL;
724 return af->fp;
727 static int aio_cmd_getfd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
729 AioFile *af = Jim_CmdPrivData(interp);
731 fflush(af->fp);
732 Jim_SetResultInt(interp, fileno(af->fp));
734 return JIM_OK;
737 static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
739 AioFile *af = Jim_CmdPrivData(interp);
740 jim_wide count = 0;
741 jim_wide maxlen = JIM_WIDE_MAX;
742 AioFile *outf = Jim_AioFile(interp, argv[0]);
744 if (outf == NULL) {
745 return JIM_ERR;
748 if (argc == 2) {
749 if (Jim_GetWide(interp, argv[1], &maxlen) != JIM_OK) {
750 return JIM_ERR;
754 while (count < maxlen) {
755 /* A reasonable compromise between stack size and speed */
756 char buf[AIO_BUF_LEN];
757 jim_wide len = maxlen - count;
758 if (len > sizeof(buf)) {
759 len = sizeof(buf);
762 len = af->fops->reader(af, buf, len);
763 if (len <= 0) {
764 break;
766 if (outf->fops->writer(outf, buf, len) != len) {
767 break;
769 count += len;
772 if (JimCheckStreamError(interp, af) || JimCheckStreamError(interp, outf)) {
773 return JIM_ERR;
776 Jim_SetResultInt(interp, count);
778 return JIM_OK;
781 static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
783 AioFile *af = Jim_CmdPrivData(interp);
784 char buf[AIO_BUF_LEN];
785 Jim_Obj *objPtr;
786 int len;
788 errno = 0;
790 objPtr = Jim_NewStringObj(interp, NULL, 0);
791 while (1) {
792 buf[AIO_BUF_LEN - 1] = '_';
794 if (af->fops->getline(af, buf, AIO_BUF_LEN) == NULL)
795 break;
797 if (buf[AIO_BUF_LEN - 1] == '\0' && buf[AIO_BUF_LEN - 2] != '\n') {
798 Jim_AppendString(interp, objPtr, buf, AIO_BUF_LEN - 1);
800 else {
801 len = strlen(buf);
803 if (len && (buf[len - 1] == '\n')) {
804 /* strip "\n" */
805 len--;
808 Jim_AppendString(interp, objPtr, buf, len);
809 break;
813 if (JimCheckStreamError(interp, af)) {
814 /* I/O error */
815 Jim_FreeNewObj(interp, objPtr);
816 return JIM_ERR;
819 if (argc) {
820 if (Jim_SetVariable(interp, argv[0], objPtr) != JIM_OK) {
821 Jim_FreeNewObj(interp, objPtr);
822 return JIM_ERR;
825 len = Jim_Length(objPtr);
827 if (len == 0 && feof(af->fp)) {
828 /* On EOF returns -1 if varName was specified */
829 len = -1;
831 Jim_SetResultInt(interp, len);
833 else {
834 Jim_SetResult(interp, objPtr);
836 return JIM_OK;
839 static int aio_cmd_puts(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
841 AioFile *af = Jim_CmdPrivData(interp);
842 int wlen;
843 const char *wdata;
844 Jim_Obj *strObj;
846 if (argc == 2) {
847 if (!Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
848 return -1;
850 strObj = argv[1];
852 else {
853 strObj = argv[0];
856 wdata = Jim_GetString(strObj, &wlen);
857 if (af->fops->writer(af, wdata, wlen) == wlen) {
858 if (argc == 2 || af->fops->writer(af, "\n", 1) == 1) {
859 return JIM_OK;
862 JimAioSetError(interp, af->filename);
863 return JIM_ERR;
866 static int aio_cmd_isatty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
868 #ifdef HAVE_ISATTY
869 AioFile *af = Jim_CmdPrivData(interp);
870 Jim_SetResultInt(interp, isatty(fileno(af->fp)));
871 #else
872 Jim_SetResultInt(interp, 0);
873 #endif
875 return JIM_OK;
878 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
879 static int aio_cmd_recvfrom(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
881 AioFile *af = Jim_CmdPrivData(interp);
882 char *buf;
883 union sockaddr_any sa;
884 long len;
885 socklen_t salen = sizeof(sa);
886 int rlen;
888 if (Jim_GetLong(interp, argv[0], &len) != JIM_OK) {
889 return JIM_ERR;
892 buf = Jim_Alloc(len + 1);
894 rlen = recvfrom(fileno(af->fp), buf, len, 0, &sa.sa, &salen);
895 if (rlen < 0) {
896 Jim_Free(buf);
897 JimAioSetError(interp, NULL);
898 return JIM_ERR;
900 buf[rlen] = 0;
901 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, rlen));
903 if (argc > 1) {
904 return JimSetVariableSocketAddress(interp, argv[1], &sa, salen);
907 return JIM_OK;
911 static int aio_cmd_sendto(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
913 AioFile *af = Jim_CmdPrivData(interp);
914 int wlen;
915 int len;
916 const char *wdata;
917 union sockaddr_any sa;
918 const char *addr = Jim_String(argv[1]);
919 socklen_t salen;
921 if (JimParseSocketAddress(interp, af->addr_family, addr, &sa, &salen) != JIM_OK) {
922 return JIM_ERR;
924 wdata = Jim_GetString(argv[0], &wlen);
926 /* Note that we don't validate the socket type. Rely on sendto() failing if appropriate */
927 len = sendto(fileno(af->fp), wdata, wlen, 0, &sa.sa, salen);
928 if (len < 0) {
929 JimAioSetError(interp, NULL);
930 return JIM_ERR;
932 Jim_SetResultInt(interp, len);
933 return JIM_OK;
936 static int aio_cmd_accept(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
938 AioFile *af = Jim_CmdPrivData(interp);
939 int sock;
940 union sockaddr_any sa;
941 socklen_t salen = sizeof(sa);
943 sock = accept(af->fd, &sa.sa, &salen);
944 if (sock < 0) {
945 JimAioSetError(interp, NULL);
946 return JIM_ERR;
949 if (argc > 0) {
950 if (JimSetVariableSocketAddress(interp, argv[0], &sa, salen) != JIM_OK) {
951 return JIM_ERR;
955 /* Create the file command */
956 return JimMakeChannel(interp, NULL, sock, Jim_NewStringObj(interp, "accept", -1),
957 "aio.sockstream%ld", af->addr_family, "r+", AIO_NODELETE) ? JIM_OK : JIM_ERR;
960 static int aio_cmd_sockname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
962 AioFile *af = Jim_CmdPrivData(interp);
963 Jim_Obj *objPtr = aio_sockname(interp, af);
965 if (objPtr == NULL) {
966 JimAioSetError(interp, NULL);
967 return JIM_ERR;
969 Jim_SetResult(interp, objPtr);
970 return JIM_OK;
973 static int aio_cmd_peername(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
975 AioFile *af = Jim_CmdPrivData(interp);
976 union sockaddr_any sa;
977 socklen_t salen = sizeof(sa);
979 if (getpeername(af->fd, &sa.sa, &salen) < 0) {
980 JimAioSetError(interp, NULL);
981 return JIM_ERR;
983 Jim_SetResult(interp, JimFormatSocketAddress(interp, &sa, salen));
984 return JIM_OK;
987 static int aio_cmd_listen(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
989 AioFile *af = Jim_CmdPrivData(interp);
990 long backlog;
992 if (Jim_GetLong(interp, argv[0], &backlog) != JIM_OK) {
993 return JIM_ERR;
996 if (listen(af->fd, backlog)) {
997 JimAioSetError(interp, NULL);
998 return JIM_ERR;
1001 return JIM_OK;
1003 #endif /* JIM_BOOTSTRAP */
1005 static int aio_cmd_flush(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1007 AioFile *af = Jim_CmdPrivData(interp);
1009 if (fflush(af->fp) == EOF) {
1010 JimAioSetError(interp, af->filename);
1011 return JIM_ERR;
1013 return JIM_OK;
1016 static int aio_cmd_eof(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1018 AioFile *af = Jim_CmdPrivData(interp);
1020 Jim_SetResultInt(interp, feof(af->fp));
1021 return JIM_OK;
1024 static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1026 if (argc == 3) {
1027 int option = -1;
1028 #if defined(HAVE_SOCKETS)
1029 static const char * const options[] = { "r", "w", "-nodelete", NULL };
1030 enum { OPT_R, OPT_W, OPT_NODELETE };
1031 AioFile *af = Jim_CmdPrivData(interp);
1033 if (Jim_GetEnum(interp, argv[2], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
1034 return JIM_ERR;
1036 #endif
1037 switch (option) {
1038 #if defined(HAVE_SHUTDOWN)
1039 case OPT_R:
1040 case OPT_W:
1041 if (shutdown(af->fd, option == OPT_R ? SHUT_RD : SHUT_WR) == 0) {
1042 return JIM_OK;
1044 JimAioSetError(interp, NULL);
1045 return JIM_ERR;
1046 #endif
1047 #if UNIX_SOCKETS
1048 case OPT_NODELETE:
1049 if (af->addr_family == PF_UNIX) {
1050 af->openFlags |= AIO_NODELETE;
1051 break;
1053 /* fall through */
1054 #endif
1055 default:
1056 Jim_SetResultString(interp, "not supported", -1);
1057 return JIM_ERR;
1061 return Jim_DeleteCommand(interp, Jim_String(argv[0]));
1064 static int aio_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1066 AioFile *af = Jim_CmdPrivData(interp);
1067 int orig = SEEK_SET;
1068 jim_wide offset;
1070 if (argc == 2) {
1071 if (Jim_CompareStringImmediate(interp, argv[1], "start"))
1072 orig = SEEK_SET;
1073 else if (Jim_CompareStringImmediate(interp, argv[1], "current"))
1074 orig = SEEK_CUR;
1075 else if (Jim_CompareStringImmediate(interp, argv[1], "end"))
1076 orig = SEEK_END;
1077 else {
1078 return -1;
1081 if (Jim_GetWide(interp, argv[0], &offset) != JIM_OK) {
1082 return JIM_ERR;
1084 if (fseeko(af->fp, offset, orig) == -1) {
1085 JimAioSetError(interp, af->filename);
1086 return JIM_ERR;
1088 return JIM_OK;
1091 static int aio_cmd_tell(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1093 AioFile *af = Jim_CmdPrivData(interp);
1095 Jim_SetResultInt(interp, ftello(af->fp));
1096 return JIM_OK;
1099 static int aio_cmd_filename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1101 AioFile *af = Jim_CmdPrivData(interp);
1103 Jim_SetResult(interp, af->filename);
1104 return JIM_OK;
1107 #ifdef O_NDELAY
1108 static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1110 AioFile *af = Jim_CmdPrivData(interp);
1112 int fmode = fcntl(af->fd, F_GETFL);
1114 if (argc) {
1115 long nb;
1117 if (Jim_GetLong(interp, argv[0], &nb) != JIM_OK) {
1118 return JIM_ERR;
1120 if (nb) {
1121 fmode |= O_NDELAY;
1123 else {
1124 fmode &= ~O_NDELAY;
1126 (void)fcntl(af->fd, F_SETFL, fmode);
1128 Jim_SetResultInt(interp, (fmode & O_NONBLOCK) ? 1 : 0);
1129 return JIM_OK;
1131 #endif
1133 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
1134 #define SOCKOPT_BOOL 0
1135 #define SOCKOPT_INT 1
1136 #define SOCKOPT_TIMEVAL 2 /* not currently supported */
1138 static const struct sockopt_def {
1139 const char *name;
1140 int level;
1141 int opt;
1142 int type; /* SOCKOPT_xxx */
1143 } sockopts[] = {
1144 #ifdef SOL_SOCKET
1145 #ifdef SO_BROADCAST
1146 { "broadcast", SOL_SOCKET, SO_BROADCAST },
1147 #endif
1148 #ifdef SO_DEBUG
1149 { "debug", SOL_SOCKET, SO_DEBUG },
1150 #endif
1151 #ifdef SO_KEEPALIVE
1152 { "keepalive", SOL_SOCKET, SO_KEEPALIVE },
1153 #endif
1154 #ifdef SO_NOSIGPIPE
1155 { "nosigpipe", SOL_SOCKET, SO_NOSIGPIPE },
1156 #endif
1157 #ifdef SO_OOBINLINE
1158 { "oobinline", SOL_SOCKET, SO_OOBINLINE },
1159 #endif
1160 #ifdef SO_SNDBUF
1161 { "sndbuf", SOL_SOCKET, SO_SNDBUF, SOCKOPT_INT },
1162 #endif
1163 #ifdef SO_RCVBUF
1164 { "rcvbuf", SOL_SOCKET, SO_RCVBUF, SOCKOPT_INT },
1165 #endif
1166 #if 0 && defined(SO_SNDTIMEO)
1167 { "sndtimeo", SOL_SOCKET, SO_SNDTIMEO, SOCKOPT_TIMEVAL },
1168 #endif
1169 #if 0 && defined(SO_RCVTIMEO)
1170 { "rcvtimeo", SOL_SOCKET, SO_RCVTIMEO, SOCKOPT_TIMEVAL },
1171 #endif
1172 #endif
1173 #ifdef IPPROTO_TCP
1174 #ifdef TCP_NODELAY
1175 { "tcp_nodelay", IPPROTO_TCP, TCP_NODELAY },
1176 #endif
1177 #endif
1180 static int aio_cmd_sockopt(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1182 AioFile *af = Jim_CmdPrivData(interp);
1183 size_t i;
1185 if (argc == 0) {
1186 Jim_Obj *dictObjPtr = Jim_NewListObj(interp, NULL, 0);
1187 for (i = 0; i < sizeof(sockopts) / sizeof(*sockopts); i++) {
1188 int value = 0;
1189 socklen_t len = sizeof(value);
1190 if (getsockopt(af->fd, sockopts[i].level, sockopts[i].opt, (void *)&value, &len) == 0) {
1191 if (sockopts[i].type == SOCKOPT_BOOL) {
1192 value = !!value;
1194 Jim_ListAppendElement(interp, dictObjPtr, Jim_NewStringObj(interp, sockopts[i].name, -1));
1195 Jim_ListAppendElement(interp, dictObjPtr, Jim_NewIntObj(interp, value));
1198 Jim_SetResult(interp, dictObjPtr);
1199 return JIM_OK;
1201 if (argc == 1) {
1202 return -1;
1205 /* Set an option */
1206 for (i = 0; i < sizeof(sockopts) / sizeof(*sockopts); i++) {
1207 if (strcmp(Jim_String(argv[0]), sockopts[i].name) == 0) {
1208 int on;
1209 if (sockopts[i].type == SOCKOPT_BOOL) {
1210 if (Jim_GetBoolean(interp, argv[1], &on) != JIM_OK) {
1211 return JIM_ERR;
1214 else {
1215 long longval;
1216 if (Jim_GetLong(interp, argv[1], &longval) != JIM_OK) {
1217 return JIM_ERR;
1219 on = longval;
1221 if (setsockopt(af->fd, sockopts[i].level, sockopts[i].opt, (void *)&on, sizeof(on)) < 0) {
1222 Jim_SetResultFormatted(interp, "Failed to set %#s: %s", argv[0], strerror(errno));
1223 return JIM_ERR;
1225 return JIM_OK;
1228 /* Not found */
1229 Jim_SetResultFormatted(interp, "Unknown sockopt %#s", argv[0]);
1230 return JIM_ERR;
1232 #endif /* JIM_BOOTSTRAP */
1234 #ifdef HAVE_FSYNC
1235 static int aio_cmd_sync(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1237 AioFile *af = Jim_CmdPrivData(interp);
1239 fflush(af->fp);
1240 fsync(af->fd);
1241 return JIM_OK;
1243 #endif
1245 static int aio_cmd_buffering(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1247 AioFile *af = Jim_CmdPrivData(interp);
1249 static const char * const options[] = {
1250 "none",
1251 "line",
1252 "full",
1253 NULL
1255 enum
1257 OPT_NONE,
1258 OPT_LINE,
1259 OPT_FULL,
1261 int option;
1263 if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
1264 return JIM_ERR;
1266 switch (option) {
1267 case OPT_NONE:
1268 setvbuf(af->fp, NULL, _IONBF, 0);
1269 break;
1270 case OPT_LINE:
1271 setvbuf(af->fp, NULL, _IOLBF, BUFSIZ);
1272 break;
1273 case OPT_FULL:
1274 setvbuf(af->fp, NULL, _IOFBF, BUFSIZ);
1275 break;
1277 return JIM_OK;
1280 #ifdef jim_ext_eventloop
1281 static void JimAioFileEventFinalizer(Jim_Interp *interp, void *clientData)
1283 Jim_Obj **objPtrPtr = clientData;
1285 Jim_DecrRefCount(interp, *objPtrPtr);
1286 *objPtrPtr = NULL;
1289 static int JimAioFileEventHandler(Jim_Interp *interp, void *clientData, int mask)
1291 Jim_Obj **objPtrPtr = clientData;
1293 return Jim_EvalObjBackground(interp, *objPtrPtr);
1296 static int aio_eventinfo(Jim_Interp *interp, AioFile * af, unsigned mask, Jim_Obj **scriptHandlerObj,
1297 int argc, Jim_Obj * const *argv)
1299 if (argc == 0) {
1300 /* Return current script */
1301 if (*scriptHandlerObj) {
1302 Jim_SetResult(interp, *scriptHandlerObj);
1304 return JIM_OK;
1307 if (*scriptHandlerObj) {
1308 /* Delete old handler */
1309 Jim_DeleteFileHandler(interp, af->fd, mask);
1312 /* Now possibly add the new script(s) */
1313 if (Jim_Length(argv[0]) == 0) {
1314 /* Empty script, so done */
1315 return JIM_OK;
1318 /* A new script to add */
1319 Jim_IncrRefCount(argv[0]);
1320 *scriptHandlerObj = argv[0];
1322 Jim_CreateFileHandler(interp, af->fd, mask,
1323 JimAioFileEventHandler, scriptHandlerObj, JimAioFileEventFinalizer);
1325 return JIM_OK;
1328 static int aio_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1330 AioFile *af = Jim_CmdPrivData(interp);
1332 return aio_eventinfo(interp, af, JIM_EVENT_READABLE, &af->rEvent, argc, argv);
1335 static int aio_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1337 AioFile *af = Jim_CmdPrivData(interp);
1339 return aio_eventinfo(interp, af, JIM_EVENT_WRITABLE, &af->wEvent, argc, argv);
1342 static int aio_cmd_onexception(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1344 AioFile *af = Jim_CmdPrivData(interp);
1346 return aio_eventinfo(interp, af, JIM_EVENT_EXCEPTION, &af->eEvent, argc, argv);
1348 #endif
1350 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1351 static int aio_cmd_ssl(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1353 AioFile *af = Jim_CmdPrivData(interp);
1354 SSL *ssl;
1355 SSL_CTX *ssl_ctx;
1356 int server = 0;
1358 if (argc == 5) {
1359 if (!Jim_CompareStringImmediate(interp, argv[2], "-server")) {
1360 return JIM_ERR;
1362 server = 1;
1364 else if (argc != 2) {
1365 return -1;
1368 if (af->ssl) {
1369 Jim_SetResultFormatted(interp, "%#s: stream is already ssl", argv[0]);
1370 return JIM_ERR;
1373 ssl_ctx = JimAioSslCtx(interp);
1374 if (ssl_ctx == NULL) {
1375 return JIM_ERR;
1378 ssl = SSL_new(ssl_ctx);
1379 if (ssl == NULL) {
1380 goto out;
1383 SSL_set_cipher_list(ssl, "ALL");
1385 if (SSL_set_fd(ssl, fileno(af->fp)) == 0) {
1386 goto out;
1389 if (server) {
1390 if (SSL_use_certificate_file(ssl, Jim_String(argv[3]), SSL_FILETYPE_PEM) != 1) {
1391 goto out;
1394 if (SSL_use_PrivateKey_file(ssl, Jim_String(argv[4]), SSL_FILETYPE_PEM) != 1) {
1395 goto out;
1398 if (SSL_accept(ssl) != 1) {
1399 goto out;
1402 else {
1403 if (SSL_connect(ssl) != 1) {
1404 goto out;
1408 af->ssl = ssl;
1409 af->fops = &ssl_fops;
1411 /* Set the command name as the result */
1412 Jim_SetResult(interp, argv[0]);
1414 return JIM_OK;
1416 out:
1417 if (ssl) {
1418 SSL_free(ssl);
1420 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1421 return JIM_ERR;
1424 static int aio_cmd_verify(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1426 AioFile *af = Jim_CmdPrivData(interp);
1427 int ret;
1429 if (!af->fops->verify) {
1430 return JIM_OK;
1433 ret = af->fops->verify(af);
1434 if (ret != JIM_OK) {
1435 if (JimCheckStreamError(interp, af) == JIM_OK) {
1436 Jim_SetResultString(interp, "failed to verify the connection authenticity", -1);
1439 return ret;
1441 #endif /* JIM_BOOTSTRAP */
1443 #if defined(HAVE_STRUCT_FLOCK) && !defined(JIM_BOOTSTRAP)
1444 static int aio_cmd_lock(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1446 AioFile *af = Jim_CmdPrivData(interp);
1447 struct flock fl;
1448 int lockmode = F_SETLK;
1450 if (argc == 1) {
1451 if (!Jim_CompareStringImmediate(interp, argv[0], "-wait")) {
1452 return -1;
1454 lockmode = F_SETLKW;
1457 fl.l_start = 0;
1458 fl.l_len = 0;
1459 fl.l_type = F_WRLCK;
1460 fl.l_whence = SEEK_SET;
1462 switch (fcntl(af->fd, lockmode, &fl))
1464 case 0:
1465 Jim_SetResultInt(interp, 1);
1466 break;
1467 case -1:
1468 if (errno == EACCES || errno == EAGAIN)
1469 Jim_SetResultInt(interp, 0);
1470 else
1472 Jim_SetResultFormatted(interp, "lock failed: %s",
1473 strerror(errno));
1474 return JIM_ERR;
1476 break;
1477 default:
1478 Jim_SetResultInt(interp, 0);
1479 break;
1482 return JIM_OK;
1485 static int aio_cmd_unlock(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1487 AioFile *af = Jim_CmdPrivData(interp);
1488 struct flock fl;
1489 fl.l_start = 0;
1490 fl.l_len = 0;
1491 fl.l_type = F_UNLCK;
1492 fl.l_whence = SEEK_SET;
1494 Jim_SetResultInt(interp, fcntl(af->fd, F_SETLK, &fl) == 0);
1495 return JIM_OK;
1497 #endif /* JIM_BOOTSTRAP */
1499 #if defined(HAVE_TERMIOS_H) && !defined(JIM_BOOTSTRAP)
1500 static int aio_cmd_tty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1502 AioFile *af = Jim_CmdPrivData(interp);
1503 Jim_Obj *dictObjPtr;
1504 int ret;
1506 if (argc == 0) {
1507 /* get the current settings as a dictionary */
1508 dictObjPtr = Jim_GetTtySettings(interp, af->fd);
1509 if (dictObjPtr == NULL) {
1510 JimAioSetError(interp, NULL);
1511 return JIM_ERR;
1513 Jim_SetResult(interp, dictObjPtr);
1514 return JIM_OK;
1517 if (argc > 1) {
1518 /* Convert name value arguments to a dictionary */
1519 dictObjPtr = Jim_NewListObj(interp, argv, argc);
1521 else {
1522 /* The settings are already given as a list */
1523 dictObjPtr = argv[0];
1525 Jim_IncrRefCount(dictObjPtr);
1527 if (Jim_ListLength(interp, dictObjPtr) % 2) {
1528 /* Must be a valid dictionary */
1529 Jim_DecrRefCount(interp, dictObjPtr);
1530 return -1;
1533 ret = Jim_SetTtySettings(interp, af->fd, dictObjPtr);
1534 if (ret < 0) {
1535 JimAioSetError(interp, NULL);
1536 ret = JIM_ERR;
1538 Jim_DecrRefCount(interp, dictObjPtr);
1540 return ret;
1542 #endif /* JIM_BOOTSTRAP */
1544 static const jim_subcmd_type aio_command_table[] = {
1545 { "read",
1546 "?-nonewline? ?len?",
1547 aio_cmd_read,
1550 /* Description: Read and return bytes from the stream. To eof if no len. */
1552 { "copyto",
1553 "handle ?size?",
1554 aio_cmd_copy,
1557 /* Description: Copy up to 'size' bytes to the given filehandle, or to eof if no size. */
1559 { "getfd",
1560 NULL,
1561 aio_cmd_getfd,
1564 /* Description: Internal command to return the underlying file descriptor. */
1566 { "gets",
1567 "?var?",
1568 aio_cmd_gets,
1571 /* Description: Read one line and return it or store it in the var */
1573 { "puts",
1574 "?-nonewline? str",
1575 aio_cmd_puts,
1578 /* Description: Write the string, with newline unless -nonewline */
1580 { "isatty",
1581 NULL,
1582 aio_cmd_isatty,
1585 /* Description: Is the file descriptor a tty? */
1587 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
1588 { "recvfrom",
1589 "len ?addrvar?",
1590 aio_cmd_recvfrom,
1593 /* Description: Receive up to 'len' bytes on the socket. Sets 'addrvar' with receive address, if set */
1595 { "sendto",
1596 "str address",
1597 aio_cmd_sendto,
1600 /* Description: Send 'str' to the given address (dgram only) */
1602 { "accept",
1603 "?addrvar?",
1604 aio_cmd_accept,
1607 /* Description: Server socket only: Accept a connection and return stream */
1609 { "listen",
1610 "backlog",
1611 aio_cmd_listen,
1614 /* Description: Set the listen backlog for server socket */
1616 { "sockopt",
1617 "?opt 0|1?",
1618 aio_cmd_sockopt,
1621 /* Description: Return a dictionary of sockopts, or set the value of a sockopt */
1623 { "sockname",
1624 NULL,
1625 aio_cmd_sockname,
1628 /* Description: Returns the local address of the socket, if any */
1630 { "peername",
1631 NULL,
1632 aio_cmd_peername,
1635 /* Description: Returns the remote address of the socket, if any */
1637 #endif /* JIM_BOOTSTRAP */
1638 { "flush",
1639 NULL,
1640 aio_cmd_flush,
1643 /* Description: Flush the stream */
1645 { "eof",
1646 NULL,
1647 aio_cmd_eof,
1650 /* Description: Returns 1 if stream is at eof */
1652 { "close",
1653 "?r(ead)|w(rite)?",
1654 aio_cmd_close,
1657 JIM_MODFLAG_FULLARGV,
1658 /* Description: Closes the stream. */
1660 { "seek",
1661 "offset ?start|current|end",
1662 aio_cmd_seek,
1665 /* Description: Seeks in the stream (default 'current') */
1667 { "tell",
1668 NULL,
1669 aio_cmd_tell,
1672 /* Description: Returns the current seek position */
1674 { "filename",
1675 NULL,
1676 aio_cmd_filename,
1679 /* Description: Returns the original filename */
1681 #ifdef O_NDELAY
1682 { "ndelay",
1683 "?0|1?",
1684 aio_cmd_ndelay,
1687 /* Description: Set O_NDELAY (if arg). Returns current/new setting. */
1689 #endif
1690 #ifdef HAVE_FSYNC
1691 { "sync",
1692 NULL,
1693 aio_cmd_sync,
1696 /* Description: Flush and fsync() the stream */
1698 #endif
1699 { "buffering",
1700 "none|line|full",
1701 aio_cmd_buffering,
1704 /* Description: Sets buffering */
1706 #ifdef jim_ext_eventloop
1707 { "readable",
1708 "?readable-script?",
1709 aio_cmd_readable,
1712 /* Description: Returns script, or invoke readable-script when readable, {} to remove */
1714 { "writable",
1715 "?writable-script?",
1716 aio_cmd_writable,
1719 /* Description: Returns script, or invoke writable-script when writable, {} to remove */
1721 { "onexception",
1722 "?exception-script?",
1723 aio_cmd_onexception,
1726 /* Description: Returns script, or invoke exception-script when oob data, {} to remove */
1728 #endif
1729 #if !defined(JIM_BOOTSTRAP)
1730 #if defined(JIM_SSL)
1731 { "ssl",
1732 "?-server cert priv?",
1733 aio_cmd_ssl,
1736 JIM_MODFLAG_FULLARGV
1737 /* Description: Wraps a stream socket with SSL/TLS and returns a new channel */
1739 { "verify",
1740 NULL,
1741 aio_cmd_verify,
1744 /* Description: Verifies the certificate of a SSL/TLS channel */
1746 #endif
1747 #if defined(HAVE_STRUCT_FLOCK)
1748 { "lock ?-wait?",
1749 NULL,
1750 aio_cmd_lock,
1753 /* Description: Attempt to get a lock, possibly waiting */
1755 { "unlock",
1756 NULL,
1757 aio_cmd_unlock,
1760 /* Description: Relase a lock. */
1762 #endif
1763 #if defined(HAVE_TERMIOS_H)
1764 { "tty",
1765 "?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?",
1766 aio_cmd_tty,
1769 /* Description: Get or set tty settings - valid only on a tty */
1771 #endif
1772 #endif /* JIM_BOOTSTRAP */
1773 { NULL }
1776 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1778 return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, aio_command_table, argc, argv), argc, argv);
1781 static int JimAioOpenCommand(Jim_Interp *interp, int argc,
1782 Jim_Obj *const *argv)
1784 const char *mode;
1786 if (argc != 2 && argc != 3) {
1787 Jim_WrongNumArgs(interp, 1, argv, "filename ?mode?");
1788 return JIM_ERR;
1791 mode = (argc == 3) ? Jim_String(argv[2]) : "r";
1793 #ifdef jim_ext_tclcompat
1795 const char *filename = Jim_String(argv[1]);
1797 /* If the filename starts with '|', use popen instead */
1798 if (*filename == '|') {
1799 Jim_Obj *evalObj[3];
1801 evalObj[0] = Jim_NewStringObj(interp, "::popen", -1);
1802 evalObj[1] = Jim_NewStringObj(interp, filename + 1, -1);
1803 evalObj[2] = Jim_NewStringObj(interp, mode, -1);
1805 return Jim_EvalObjVector(interp, 3, evalObj);
1808 #endif
1809 return JimMakeChannel(interp, NULL, -1, argv[1], "aio.handle%ld", 0, mode, 0) ? JIM_OK : JIM_ERR;
1812 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1813 static void JimAioSslContextDelProc(struct Jim_Interp *interp, void *privData)
1815 SSL_CTX_free((SSL_CTX *)privData);
1816 ERR_free_strings();
1819 #ifdef USE_TLSv1_2_method
1820 #define TLS_method TLSv1_2_method
1821 #endif
1823 static SSL_CTX *JimAioSslCtx(Jim_Interp *interp)
1825 SSL_CTX *ssl_ctx = (SSL_CTX *)Jim_GetAssocData(interp, "ssl_ctx");
1826 if (ssl_ctx == NULL) {
1827 SSL_load_error_strings();
1828 SSL_library_init();
1829 ssl_ctx = SSL_CTX_new(TLS_method());
1830 if (ssl_ctx && SSL_CTX_set_default_verify_paths(ssl_ctx)) {
1831 SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
1832 Jim_SetAssocData(interp, "ssl_ctx", JimAioSslContextDelProc, ssl_ctx);
1833 } else {
1834 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1837 return ssl_ctx;
1839 #endif /* JIM_BOOTSTRAP */
1842 * Creates a channel for fh/fd/filename.
1844 * If fh is not NULL, uses that as the channel (and sets AIO_KEEPOPEN).
1845 * Otherwise, if fd is >= 0, uses that as the channel.
1846 * Otherwise opens 'filename' with mode 'mode'.
1848 * hdlfmt is a sprintf format for the filehandle. Anything with %ld at the end will do.
1849 * mode is used for open or fdopen.
1851 * Creates the command and sets the name as the current result.
1852 * Returns the AioFile pointer on sucess or NULL on failure.
1854 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
1855 const char *hdlfmt, int family, const char *mode, int flags)
1857 AioFile *af;
1858 char buf[AIO_CMD_LEN];
1860 snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
1861 if (!filename) {
1862 filename = Jim_NewStringObj(interp, buf, -1);
1865 Jim_IncrRefCount(filename);
1867 if (fh == NULL) {
1868 if (fd >= 0) {
1869 #ifndef JIM_ANSIC
1870 fh = fdopen(fd, mode);
1871 #endif
1873 else
1874 fh = fopen(Jim_String(filename), mode);
1876 if (fh == NULL) {
1877 JimAioSetError(interp, filename);
1878 #ifndef JIM_ANSIC
1879 if (fd >= 0) {
1880 close(fd);
1882 #endif
1883 Jim_DecrRefCount(interp, filename);
1884 return NULL;
1888 /* Create the file command */
1889 af = Jim_Alloc(sizeof(*af));
1890 memset(af, 0, sizeof(*af));
1891 af->fp = fh;
1892 af->filename = filename;
1893 af->openFlags = flags;
1894 #ifndef JIM_ANSIC
1895 af->fd = fileno(fh);
1896 #ifdef FD_CLOEXEC
1897 if ((flags & AIO_KEEPOPEN) == 0) {
1898 (void)fcntl(af->fd, F_SETFD, FD_CLOEXEC);
1900 #endif
1901 #endif
1902 af->addr_family = family;
1903 af->fops = &stdio_fops;
1904 af->ssl = NULL;
1906 Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc);
1908 /* Note that the command must use the global namespace, even if
1909 * the current namespace is something different
1911 Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, Jim_NewStringObj(interp, buf, -1)));
1913 return af;
1916 #if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && UNIX_SOCKETS)
1918 * Create a pair of channels. e.g. from pipe() or socketpair()
1920 static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename,
1921 const char *hdlfmt, int family, const char * const mode[2])
1923 if (JimMakeChannel(interp, NULL, p[0], filename, hdlfmt, family, mode[0], 0)) {
1924 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
1925 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
1926 if (JimMakeChannel(interp, NULL, p[1], filename, hdlfmt, family, mode[1], 0)) {
1927 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
1928 Jim_SetResult(interp, objPtr);
1929 return JIM_OK;
1933 /* Can only be here if fdopen() failed */
1934 close(p[0]);
1935 close(p[1]);
1936 JimAioSetError(interp, NULL);
1937 return JIM_ERR;
1939 #endif
1941 #ifdef HAVE_PIPE
1942 static int JimAioPipeCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1944 int p[2];
1945 static const char * const mode[2] = { "r", "w" };
1947 if (argc != 1) {
1948 Jim_WrongNumArgs(interp, 1, argv, "");
1949 return JIM_ERR;
1952 if (pipe(p) != 0) {
1953 JimAioSetError(interp, NULL);
1954 return JIM_ERR;
1957 return JimMakeChannelPair(interp, p, argv[0], "aio.pipe%ld", 0, mode);
1959 #endif
1961 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
1963 static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1965 const char *socktypes[] = {
1966 "unix",
1967 "unix.server",
1968 "unix.dgram",
1969 "unix.dgram.server",
1970 "dgram",
1971 "dgram.server",
1972 "stream",
1973 "stream.server",
1974 "pipe",
1975 "pair",
1976 NULL
1978 enum
1980 SOCK_UNIX,
1981 SOCK_UNIX_SERVER,
1982 SOCK_UNIX_DGRAM,
1983 SOCK_UNIX_DGRAM_SERVER,
1984 SOCK_DGRAM_CLIENT,
1985 SOCK_DGRAM_SERVER,
1986 SOCK_STREAM_CLIENT,
1987 SOCK_STREAM_SERVER,
1988 SOCK_STREAM_PIPE,
1989 SOCK_STREAM_SOCKETPAIR,
1991 int socktype;
1992 int sock;
1993 const char *addr = NULL;
1994 const char *bind_addr = NULL;
1995 const char *connect_addr = NULL;
1996 union sockaddr_any sa;
1997 socklen_t salen;
1998 int on = 1;
1999 int reuse = 0;
2000 int do_listen = 0;
2001 int family = PF_INET;
2002 int type = SOCK_STREAM;
2003 Jim_Obj *argv0 = argv[0];
2004 int ipv6 = 0;
2006 if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-ipv6")) {
2007 if (!IPV6) {
2008 Jim_SetResultString(interp, "ipv6 not supported", -1);
2009 return JIM_ERR;
2011 ipv6 = 1;
2012 family = PF_INET6;
2014 argc -= ipv6;
2015 argv += ipv6;
2017 if (argc < 2) {
2018 wrongargs:
2019 Jim_WrongNumArgs(interp, 1, &argv0, "?-ipv6? type ?address?");
2020 return JIM_ERR;
2023 if (Jim_GetEnum(interp, argv[1], socktypes, &socktype, "socket type", JIM_ERRMSG) != JIM_OK)
2024 return Jim_CheckShowCommands(interp, argv[1], socktypes);
2026 Jim_SetEmptyResult(interp);
2028 if (argc > 2) {
2029 addr = Jim_String(argv[2]);
2032 #if defined(HAVE_SOCKETPAIR) && UNIX_SOCKETS
2033 if (socktype == SOCK_STREAM_SOCKETPAIR) {
2034 int p[2];
2035 static const char * const mode[2] = { "r+", "r+" };
2037 if (addr || ipv6) {
2038 goto wrongargs;
2041 if (socketpair(PF_UNIX, SOCK_STREAM, 0, p) < 0) {
2042 JimAioSetError(interp, NULL);
2043 return JIM_ERR;
2045 return JimMakeChannelPair(interp, p, argv[1], "aio.sockpair%ld", PF_UNIX, mode);
2047 #endif
2049 #if defined(HAVE_PIPE)
2050 if (socktype == SOCK_STREAM_PIPE) {
2051 if (addr || ipv6) {
2052 goto wrongargs;
2054 return JimAioPipeCommand(interp, 1, &argv[1]);
2056 #endif
2058 /* Now all these socket types are very similar */
2059 switch (socktype) {
2060 case SOCK_DGRAM_CLIENT:
2061 connect_addr = addr;
2062 type = SOCK_DGRAM;
2063 break;
2065 case SOCK_STREAM_CLIENT:
2066 if (addr == NULL) {
2067 goto wrongargs;
2069 connect_addr = addr;
2070 break;
2072 case SOCK_STREAM_SERVER:
2073 if (addr == NULL) {
2074 goto wrongargs;
2076 bind_addr = addr;
2077 reuse = 1;
2078 do_listen = 1;
2079 break;
2081 case SOCK_DGRAM_SERVER:
2082 if (addr == NULL) {
2083 goto wrongargs;
2085 bind_addr = addr;
2086 type = SOCK_DGRAM;
2087 reuse = 1;
2088 break;
2090 #if UNIX_SOCKETS
2091 case SOCK_UNIX:
2092 if (addr == NULL) {
2093 goto wrongargs;
2095 connect_addr = addr;
2096 family = PF_UNIX;
2097 break;
2099 case SOCK_UNIX_DGRAM:
2100 connect_addr = addr;
2101 type = SOCK_DGRAM;
2102 family = PF_UNIX;
2103 /* A dgram unix domain socket client needs to bind
2104 * to a temporary address to allow the server to
2105 * send responses
2108 int tmpfd = Jim_MakeTempFile(interp, NULL, 1);
2109 if (tmpfd < 0) {
2110 return JIM_ERR;
2112 close(tmpfd);
2113 /* This will be valid until a result is next set, which is long enough here */
2114 bind_addr = Jim_String(Jim_GetResult(interp));
2116 break;
2118 case SOCK_UNIX_SERVER:
2119 if (addr == NULL) {
2120 goto wrongargs;
2122 bind_addr = addr;
2123 family = PF_UNIX;
2124 do_listen = 1;
2125 break;
2127 case SOCK_UNIX_DGRAM_SERVER:
2128 if (addr == NULL) {
2129 goto wrongargs;
2131 bind_addr = addr;
2132 type = SOCK_DGRAM;
2133 family = PF_UNIX;
2134 break;
2135 #endif
2137 default:
2138 Jim_SetResultString(interp, "Unsupported socket type", -1);
2139 return JIM_ERR;
2142 /* Now do all the steps necessary for the given socket type */
2143 sock = socket(family, type, 0);
2144 if (sock < 0) {
2145 JimAioSetError(interp, NULL);
2146 return JIM_ERR;
2148 if (bind_addr) {
2149 if (JimParseSocketAddress(interp, family, bind_addr, &sa, &salen) != JIM_OK) {
2150 close(sock);
2151 return JIM_ERR;
2153 if (reuse) {
2154 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
2156 if (bind(sock, &sa.sa, salen)) {
2157 Jim_SetResultFormatted(interp, "%s: bind: %s", bind_addr, strerror(errno));
2158 close(sock);
2159 return JIM_ERR;
2162 if (connect_addr) {
2163 if (JimParseSocketAddress(interp, family, connect_addr, &sa, &salen) != JIM_OK) {
2164 close(sock);
2165 return JIM_ERR;
2167 if (connect(sock, &sa.sa, salen)) {
2168 Jim_SetResultFormatted(interp, "%s: connect: %s", connect_addr, strerror(errno));
2169 close(sock);
2170 return JIM_ERR;
2173 if (do_listen) {
2174 if (listen(sock, 5)) {
2175 Jim_SetResultFormatted(interp, "listen: %s", strerror(errno));
2176 close(sock);
2177 return JIM_ERR;
2181 return JimMakeChannel(interp, NULL, sock, argv[1], "aio.sock%ld", family, "r+", 0) ? JIM_OK : JIM_ERR;
2183 #endif /* JIM_BOOTSTRAP */
2185 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
2186 static int JimAioLoadSSLCertsCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2188 SSL_CTX *ssl_ctx;
2190 if (argc != 2) {
2191 Jim_WrongNumArgs(interp, 1, argv, "dir");
2192 return JIM_ERR;
2195 ssl_ctx = JimAioSslCtx(interp);
2196 if (!ssl_ctx) {
2197 return JIM_ERR;
2199 if (SSL_CTX_load_verify_locations(ssl_ctx, NULL, Jim_String(argv[1])) == 1) {
2200 return JIM_OK;
2202 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
2203 return JIM_ERR;
2205 #endif /* JIM_BOOTSTRAP */
2207 int Jim_aioInit(Jim_Interp *interp)
2209 if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG))
2210 return JIM_ERR;
2212 #if defined(JIM_SSL)
2213 Jim_CreateCommand(interp, "load_ssl_certs", JimAioLoadSSLCertsCommand, NULL, NULL);
2214 #endif
2216 Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL);
2217 #ifdef HAVE_SOCKETS
2218 Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL);
2219 #endif
2220 #ifdef HAVE_PIPE
2221 Jim_CreateCommand(interp, "pipe", JimAioPipeCommand, NULL, NULL);
2222 #endif
2224 /* Create filehandles for stdin, stdout and stderr */
2225 JimMakeChannel(interp, stdin, -1, NULL, "stdin", 0, "r", AIO_KEEPOPEN);
2226 JimMakeChannel(interp, stdout, -1, NULL, "stdout", 0, "w", AIO_KEEPOPEN);
2227 JimMakeChannel(interp, stderr, -1, NULL, "stderr", 0, "w", AIO_KEEPOPEN);
2229 return JIM_OK;