scan: Fix a utf-8 bug for string length
[jimtcl.git] / jim-aio.c
blob1e3a67950490d48268f0e4b9a81da805e31bf226
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 break;
547 return Jim_NewStringObj(interp, addr, addrlen);
550 static int JimSetVariableSocketAddress(Jim_Interp *interp, Jim_Obj *varObjPtr, const union sockaddr_any *sa, socklen_t salen)
552 int ret;
553 Jim_Obj *objPtr = JimFormatSocketAddress(interp, sa, salen);
554 Jim_IncrRefCount(objPtr);
555 ret = Jim_SetVariable(interp, varObjPtr, objPtr);
556 Jim_DecrRefCount(interp, objPtr);
557 return ret;
560 static Jim_Obj *aio_sockname(Jim_Interp *interp, AioFile *af)
562 union sockaddr_any sa;
563 socklen_t salen = sizeof(sa);
565 if (getsockname(af->fd, &sa.sa, &salen) < 0) {
566 return NULL;
568 return JimFormatSocketAddress(interp, &sa, salen);
570 #endif /* JIM_BOOTSTRAP */
572 static const char *JimAioErrorString(AioFile *af)
574 if (af && af->fops)
575 return af->fops->strerror(af);
577 return strerror(errno);
580 static void JimAioSetError(Jim_Interp *interp, Jim_Obj *name)
582 AioFile *af = Jim_CmdPrivData(interp);
584 if (name) {
585 Jim_SetResultFormatted(interp, "%#s: %s", name, JimAioErrorString(af));
587 else {
588 Jim_SetResultString(interp, JimAioErrorString(af), -1);
592 static int JimCheckStreamError(Jim_Interp *interp, AioFile *af)
594 int ret = af->fops->error(af);
595 if (ret) {
596 JimAioSetError(interp, af->filename);
598 return ret;
601 static void JimAioDelProc(Jim_Interp *interp, void *privData)
603 AioFile *af = privData;
605 JIM_NOTUSED(interp);
607 #if UNIX_SOCKETS
608 if (af->addr_family == PF_UNIX && (af->openFlags & AIO_NODELETE) == 0) {
609 /* If this is bound, delete the socket file now */
610 Jim_Obj *filenameObj = aio_sockname(interp, af);
611 if (filenameObj) {
612 if (Jim_Length(filenameObj)) {
613 remove(Jim_String(filenameObj));
615 Jim_FreeNewObj(interp, filenameObj);
618 #endif
620 Jim_DecrRefCount(interp, af->filename);
622 #ifdef jim_ext_eventloop
623 /* remove all existing EventHandlers */
624 Jim_DeleteFileHandler(interp, af->fd, JIM_EVENT_READABLE | JIM_EVENT_WRITABLE | JIM_EVENT_EXCEPTION);
625 #endif
627 #if defined(JIM_SSL)
628 if (af->ssl != NULL) {
629 SSL_free(af->ssl);
631 #endif
632 if (!(af->openFlags & AIO_KEEPOPEN)) {
633 fclose(af->fp);
636 Jim_Free(af);
639 static int aio_cmd_read(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
641 AioFile *af = Jim_CmdPrivData(interp);
642 char buf[AIO_BUF_LEN];
643 Jim_Obj *objPtr;
644 int nonewline = 0;
645 jim_wide neededLen = -1; /* -1 is "read as much as possible" */
647 if (argc && Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
648 nonewline = 1;
649 argv++;
650 argc--;
652 if (argc == 1) {
653 if (Jim_GetWide(interp, argv[0], &neededLen) != JIM_OK)
654 return JIM_ERR;
655 if (neededLen < 0) {
656 Jim_SetResultString(interp, "invalid parameter: negative len", -1);
657 return JIM_ERR;
660 else if (argc) {
661 return -1;
663 objPtr = Jim_NewStringObj(interp, NULL, 0);
664 while (neededLen != 0) {
665 int retval;
666 int readlen;
668 if (neededLen == -1) {
669 readlen = AIO_BUF_LEN;
671 else {
672 readlen = (neededLen > AIO_BUF_LEN ? AIO_BUF_LEN : neededLen);
674 retval = af->fops->reader(af, buf, readlen);
675 if (retval > 0) {
676 Jim_AppendString(interp, objPtr, buf, retval);
677 if (neededLen != -1) {
678 neededLen -= retval;
681 if (retval != readlen)
682 break;
684 /* Check for error conditions */
685 if (JimCheckStreamError(interp, af)) {
686 Jim_FreeNewObj(interp, objPtr);
687 return JIM_ERR;
689 if (nonewline) {
690 int len;
691 const char *s = Jim_GetString(objPtr, &len);
693 if (len > 0 && s[len - 1] == '\n') {
694 objPtr->length--;
695 objPtr->bytes[objPtr->length] = '\0';
698 Jim_SetResult(interp, objPtr);
699 return JIM_OK;
702 AioFile *Jim_AioFile(Jim_Interp *interp, Jim_Obj *command)
704 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, command, JIM_ERRMSG);
706 /* XXX: There ought to be a supported API for this */
707 if (cmdPtr && !cmdPtr->isproc && cmdPtr->u.native.cmdProc == JimAioSubCmdProc) {
708 return (AioFile *) cmdPtr->u.native.privData;
710 Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", command);
711 return NULL;
714 FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command)
716 AioFile *af;
718 af = Jim_AioFile(interp, command);
719 if (af == NULL) {
720 return NULL;
723 return af->fp;
726 static int aio_cmd_getfd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
728 AioFile *af = Jim_CmdPrivData(interp);
730 fflush(af->fp);
731 Jim_SetResultInt(interp, fileno(af->fp));
733 return JIM_OK;
736 static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
738 AioFile *af = Jim_CmdPrivData(interp);
739 jim_wide count = 0;
740 jim_wide maxlen = JIM_WIDE_MAX;
741 AioFile *outf = Jim_AioFile(interp, argv[0]);
743 if (outf == NULL) {
744 return JIM_ERR;
747 if (argc == 2) {
748 if (Jim_GetWide(interp, argv[1], &maxlen) != JIM_OK) {
749 return JIM_ERR;
753 while (count < maxlen) {
754 /* A reasonable compromise between stack size and speed */
755 char buf[AIO_BUF_LEN];
756 jim_wide len = maxlen - count;
757 if (len > sizeof(buf)) {
758 len = sizeof(buf);
761 len = af->fops->reader(af, buf, len);
762 if (len <= 0) {
763 break;
765 if (outf->fops->writer(outf, buf, len) != len) {
766 break;
768 count += len;
771 if (JimCheckStreamError(interp, af) || JimCheckStreamError(interp, outf)) {
772 return JIM_ERR;
775 Jim_SetResultInt(interp, count);
777 return JIM_OK;
780 static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
782 AioFile *af = Jim_CmdPrivData(interp);
783 char buf[AIO_BUF_LEN];
784 Jim_Obj *objPtr;
785 int len;
787 errno = 0;
789 objPtr = Jim_NewStringObj(interp, NULL, 0);
790 while (1) {
791 buf[AIO_BUF_LEN - 1] = '_';
793 if (af->fops->getline(af, buf, AIO_BUF_LEN) == NULL)
794 break;
796 if (buf[AIO_BUF_LEN - 1] == '\0' && buf[AIO_BUF_LEN - 2] != '\n') {
797 Jim_AppendString(interp, objPtr, buf, AIO_BUF_LEN - 1);
799 else {
800 len = strlen(buf);
802 if (len && (buf[len - 1] == '\n')) {
803 /* strip "\n" */
804 len--;
807 Jim_AppendString(interp, objPtr, buf, len);
808 break;
812 if (JimCheckStreamError(interp, af)) {
813 /* I/O error */
814 Jim_FreeNewObj(interp, objPtr);
815 return JIM_ERR;
818 if (argc) {
819 if (Jim_SetVariable(interp, argv[0], objPtr) != JIM_OK) {
820 Jim_FreeNewObj(interp, objPtr);
821 return JIM_ERR;
824 len = Jim_Length(objPtr);
826 if (len == 0 && feof(af->fp)) {
827 /* On EOF returns -1 if varName was specified */
828 len = -1;
830 Jim_SetResultInt(interp, len);
832 else {
833 Jim_SetResult(interp, objPtr);
835 return JIM_OK;
838 static int aio_cmd_puts(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
840 AioFile *af = Jim_CmdPrivData(interp);
841 int wlen;
842 const char *wdata;
843 Jim_Obj *strObj;
845 if (argc == 2) {
846 if (!Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
847 return -1;
849 strObj = argv[1];
851 else {
852 strObj = argv[0];
855 wdata = Jim_GetString(strObj, &wlen);
856 if (af->fops->writer(af, wdata, wlen) == wlen) {
857 if (argc == 2 || af->fops->writer(af, "\n", 1) == 1) {
858 return JIM_OK;
861 JimAioSetError(interp, af->filename);
862 return JIM_ERR;
865 static int aio_cmd_isatty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
867 #ifdef HAVE_ISATTY
868 AioFile *af = Jim_CmdPrivData(interp);
869 Jim_SetResultInt(interp, isatty(fileno(af->fp)));
870 #else
871 Jim_SetResultInt(interp, 0);
872 #endif
874 return JIM_OK;
877 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
878 static int aio_cmd_recvfrom(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
880 AioFile *af = Jim_CmdPrivData(interp);
881 char *buf;
882 union sockaddr_any sa;
883 long len;
884 socklen_t salen = sizeof(sa);
885 int rlen;
887 if (Jim_GetLong(interp, argv[0], &len) != JIM_OK) {
888 return JIM_ERR;
891 buf = Jim_Alloc(len + 1);
893 rlen = recvfrom(fileno(af->fp), buf, len, 0, &sa.sa, &salen);
894 if (rlen < 0) {
895 Jim_Free(buf);
896 JimAioSetError(interp, NULL);
897 return JIM_ERR;
899 buf[rlen] = 0;
900 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, rlen));
902 if (argc > 1) {
903 return JimSetVariableSocketAddress(interp, argv[1], &sa, salen);
906 return JIM_OK;
910 static int aio_cmd_sendto(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
912 AioFile *af = Jim_CmdPrivData(interp);
913 int wlen;
914 int len;
915 const char *wdata;
916 union sockaddr_any sa;
917 const char *addr = Jim_String(argv[1]);
918 socklen_t salen;
920 if (JimParseSocketAddress(interp, af->addr_family, addr, &sa, &salen) != JIM_OK) {
921 return JIM_ERR;
923 wdata = Jim_GetString(argv[0], &wlen);
925 /* Note that we don't validate the socket type. Rely on sendto() failing if appropriate */
926 len = sendto(fileno(af->fp), wdata, wlen, 0, &sa.sa, salen);
927 if (len < 0) {
928 JimAioSetError(interp, NULL);
929 return JIM_ERR;
931 Jim_SetResultInt(interp, len);
932 return JIM_OK;
935 static int aio_cmd_accept(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
937 AioFile *af = Jim_CmdPrivData(interp);
938 int sock;
939 union sockaddr_any sa;
940 socklen_t salen = sizeof(sa);
942 sock = accept(af->fd, &sa.sa, &salen);
943 if (sock < 0) {
944 JimAioSetError(interp, NULL);
945 return JIM_ERR;
948 if (argc > 0) {
949 if (JimSetVariableSocketAddress(interp, argv[0], &sa, salen) != JIM_OK) {
950 return JIM_ERR;
954 /* Create the file command */
955 return JimMakeChannel(interp, NULL, sock, Jim_NewStringObj(interp, "accept", -1),
956 "aio.sockstream%ld", af->addr_family, "r+", AIO_NODELETE) ? JIM_OK : JIM_ERR;
959 static int aio_cmd_sockname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
961 AioFile *af = Jim_CmdPrivData(interp);
962 Jim_Obj *objPtr = aio_sockname(interp, af);
964 if (objPtr == NULL) {
965 JimAioSetError(interp, NULL);
966 return JIM_ERR;
968 Jim_SetResult(interp, objPtr);
969 return JIM_OK;
972 static int aio_cmd_peername(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
974 AioFile *af = Jim_CmdPrivData(interp);
975 union sockaddr_any sa;
976 socklen_t salen = sizeof(sa);
978 if (getpeername(af->fd, &sa.sa, &salen) < 0) {
979 JimAioSetError(interp, NULL);
980 return JIM_ERR;
982 Jim_SetResult(interp, JimFormatSocketAddress(interp, &sa, salen));
983 return JIM_OK;
986 static int aio_cmd_listen(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
988 AioFile *af = Jim_CmdPrivData(interp);
989 long backlog;
991 if (Jim_GetLong(interp, argv[0], &backlog) != JIM_OK) {
992 return JIM_ERR;
995 if (listen(af->fd, backlog)) {
996 JimAioSetError(interp, NULL);
997 return JIM_ERR;
1000 return JIM_OK;
1002 #endif /* JIM_BOOTSTRAP */
1004 static int aio_cmd_flush(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1006 AioFile *af = Jim_CmdPrivData(interp);
1008 if (fflush(af->fp) == EOF) {
1009 JimAioSetError(interp, af->filename);
1010 return JIM_ERR;
1012 return JIM_OK;
1015 static int aio_cmd_eof(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1017 AioFile *af = Jim_CmdPrivData(interp);
1019 Jim_SetResultInt(interp, feof(af->fp));
1020 return JIM_OK;
1023 static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1025 if (argc == 3) {
1026 int option = -1;
1027 #if defined(HAVE_SOCKETS)
1028 static const char * const options[] = { "r", "w", "-nodelete", NULL };
1029 enum { OPT_R, OPT_W, OPT_NODELETE };
1030 AioFile *af = Jim_CmdPrivData(interp);
1032 if (Jim_GetEnum(interp, argv[2], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
1033 return JIM_ERR;
1035 #endif
1036 switch (option) {
1037 #if defined(HAVE_SHUTDOWN)
1038 case OPT_R:
1039 case OPT_W:
1040 if (shutdown(af->fd, option == OPT_R ? SHUT_RD : SHUT_WR) == 0) {
1041 return JIM_OK;
1043 JimAioSetError(interp, NULL);
1044 return JIM_ERR;
1045 #endif
1046 #if UNIX_SOCKETS
1047 case OPT_NODELETE:
1048 if (af->addr_family == PF_UNIX) {
1049 af->openFlags |= AIO_NODELETE;
1050 break;
1052 /* fall through */
1053 #endif
1054 default:
1055 Jim_SetResultString(interp, "not supported", -1);
1056 return JIM_ERR;
1060 return Jim_DeleteCommand(interp, Jim_String(argv[0]));
1063 static int aio_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1065 AioFile *af = Jim_CmdPrivData(interp);
1066 int orig = SEEK_SET;
1067 jim_wide offset;
1069 if (argc == 2) {
1070 if (Jim_CompareStringImmediate(interp, argv[1], "start"))
1071 orig = SEEK_SET;
1072 else if (Jim_CompareStringImmediate(interp, argv[1], "current"))
1073 orig = SEEK_CUR;
1074 else if (Jim_CompareStringImmediate(interp, argv[1], "end"))
1075 orig = SEEK_END;
1076 else {
1077 return -1;
1080 if (Jim_GetWide(interp, argv[0], &offset) != JIM_OK) {
1081 return JIM_ERR;
1083 if (fseeko(af->fp, offset, orig) == -1) {
1084 JimAioSetError(interp, af->filename);
1085 return JIM_ERR;
1087 return JIM_OK;
1090 static int aio_cmd_tell(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1092 AioFile *af = Jim_CmdPrivData(interp);
1094 Jim_SetResultInt(interp, ftello(af->fp));
1095 return JIM_OK;
1098 static int aio_cmd_filename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1100 AioFile *af = Jim_CmdPrivData(interp);
1102 Jim_SetResult(interp, af->filename);
1103 return JIM_OK;
1106 #ifdef O_NDELAY
1107 static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1109 AioFile *af = Jim_CmdPrivData(interp);
1111 int fmode = fcntl(af->fd, F_GETFL);
1113 if (argc) {
1114 long nb;
1116 if (Jim_GetLong(interp, argv[0], &nb) != JIM_OK) {
1117 return JIM_ERR;
1119 if (nb) {
1120 fmode |= O_NDELAY;
1122 else {
1123 fmode &= ~O_NDELAY;
1125 (void)fcntl(af->fd, F_SETFL, fmode);
1127 Jim_SetResultInt(interp, (fmode & O_NONBLOCK) ? 1 : 0);
1128 return JIM_OK;
1130 #endif
1132 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
1133 #define SOCKOPT_BOOL 0
1134 #define SOCKOPT_INT 1
1135 #define SOCKOPT_TIMEVAL 2 /* not currently supported */
1137 static const struct sockopt_def {
1138 const char *name;
1139 int level;
1140 int opt;
1141 int type; /* SOCKOPT_xxx */
1142 } sockopts[] = {
1143 #ifdef SOL_SOCKET
1144 #ifdef SO_BROADCAST
1145 { "broadcast", SOL_SOCKET, SO_BROADCAST },
1146 #endif
1147 #ifdef SO_DEBUG
1148 { "debug", SOL_SOCKET, SO_DEBUG },
1149 #endif
1150 #ifdef SO_KEEPALIVE
1151 { "keepalive", SOL_SOCKET, SO_KEEPALIVE },
1152 #endif
1153 #ifdef SO_NOSIGPIPE
1154 { "nosigpipe", SOL_SOCKET, SO_NOSIGPIPE },
1155 #endif
1156 #ifdef SO_OOBINLINE
1157 { "oobinline", SOL_SOCKET, SO_OOBINLINE },
1158 #endif
1159 #ifdef SO_SNDBUF
1160 { "sndbuf", SOL_SOCKET, SO_SNDBUF, SOCKOPT_INT },
1161 #endif
1162 #ifdef SO_RCVBUF
1163 { "rcvbuf", SOL_SOCKET, SO_RCVBUF, SOCKOPT_INT },
1164 #endif
1165 #if 0 && defined(SO_SNDTIMEO)
1166 { "sndtimeo", SOL_SOCKET, SO_SNDTIMEO, SOCKOPT_TIMEVAL },
1167 #endif
1168 #if 0 && defined(SO_RCVTIMEO)
1169 { "rcvtimeo", SOL_SOCKET, SO_RCVTIMEO, SOCKOPT_TIMEVAL },
1170 #endif
1171 #endif
1172 #ifdef IPPROTO_TCP
1173 #ifdef TCP_NODELAY
1174 { "tcp_nodelay", IPPROTO_TCP, TCP_NODELAY },
1175 #endif
1176 #endif
1179 static int aio_cmd_sockopt(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1181 AioFile *af = Jim_CmdPrivData(interp);
1182 size_t i;
1184 if (argc == 0) {
1185 Jim_Obj *dictObjPtr = Jim_NewListObj(interp, NULL, 0);
1186 for (i = 0; i < sizeof(sockopts) / sizeof(*sockopts); i++) {
1187 int value = 0;
1188 socklen_t len = sizeof(value);
1189 if (getsockopt(af->fd, sockopts[i].level, sockopts[i].opt, (void *)&value, &len) == 0) {
1190 if (sockopts[i].type == SOCKOPT_BOOL) {
1191 value = !!value;
1193 Jim_ListAppendElement(interp, dictObjPtr, Jim_NewStringObj(interp, sockopts[i].name, -1));
1194 Jim_ListAppendElement(interp, dictObjPtr, Jim_NewIntObj(interp, value));
1197 Jim_SetResult(interp, dictObjPtr);
1198 return JIM_OK;
1200 if (argc == 1) {
1201 return -1;
1204 /* Set an option */
1205 for (i = 0; i < sizeof(sockopts) / sizeof(*sockopts); i++) {
1206 if (strcmp(Jim_String(argv[0]), sockopts[i].name) == 0) {
1207 int on;
1208 if (sockopts[i].type == SOCKOPT_BOOL) {
1209 if (Jim_GetBoolean(interp, argv[1], &on) != JIM_OK) {
1210 return JIM_ERR;
1213 else {
1214 long longval;
1215 if (Jim_GetLong(interp, argv[1], &longval) != JIM_OK) {
1216 return JIM_ERR;
1218 on = longval;
1220 if (setsockopt(af->fd, sockopts[i].level, sockopts[i].opt, (void *)&on, sizeof(on)) < 0) {
1221 Jim_SetResultFormatted(interp, "Failed to set %#s: %s", argv[0], strerror(errno));
1222 return JIM_ERR;
1224 return JIM_OK;
1227 /* Not found */
1228 Jim_SetResultFormatted(interp, "Unknown sockopt %#s", argv[0]);
1229 return JIM_ERR;
1231 #endif /* JIM_BOOTSTRAP */
1233 #ifdef HAVE_FSYNC
1234 static int aio_cmd_sync(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1236 AioFile *af = Jim_CmdPrivData(interp);
1238 fflush(af->fp);
1239 fsync(af->fd);
1240 return JIM_OK;
1242 #endif
1244 static int aio_cmd_buffering(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1246 AioFile *af = Jim_CmdPrivData(interp);
1248 static const char * const options[] = {
1249 "none",
1250 "line",
1251 "full",
1252 NULL
1254 enum
1256 OPT_NONE,
1257 OPT_LINE,
1258 OPT_FULL,
1260 int option;
1262 if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
1263 return JIM_ERR;
1265 switch (option) {
1266 case OPT_NONE:
1267 setvbuf(af->fp, NULL, _IONBF, 0);
1268 break;
1269 case OPT_LINE:
1270 setvbuf(af->fp, NULL, _IOLBF, BUFSIZ);
1271 break;
1272 case OPT_FULL:
1273 setvbuf(af->fp, NULL, _IOFBF, BUFSIZ);
1274 break;
1276 return JIM_OK;
1279 #ifdef jim_ext_eventloop
1280 static void JimAioFileEventFinalizer(Jim_Interp *interp, void *clientData)
1282 Jim_Obj **objPtrPtr = clientData;
1284 Jim_DecrRefCount(interp, *objPtrPtr);
1285 *objPtrPtr = NULL;
1288 static int JimAioFileEventHandler(Jim_Interp *interp, void *clientData, int mask)
1290 Jim_Obj **objPtrPtr = clientData;
1292 return Jim_EvalObjBackground(interp, *objPtrPtr);
1295 static int aio_eventinfo(Jim_Interp *interp, AioFile * af, unsigned mask, Jim_Obj **scriptHandlerObj,
1296 int argc, Jim_Obj * const *argv)
1298 if (argc == 0) {
1299 /* Return current script */
1300 if (*scriptHandlerObj) {
1301 Jim_SetResult(interp, *scriptHandlerObj);
1303 return JIM_OK;
1306 if (*scriptHandlerObj) {
1307 /* Delete old handler */
1308 Jim_DeleteFileHandler(interp, af->fd, mask);
1311 /* Now possibly add the new script(s) */
1312 if (Jim_Length(argv[0]) == 0) {
1313 /* Empty script, so done */
1314 return JIM_OK;
1317 /* A new script to add */
1318 Jim_IncrRefCount(argv[0]);
1319 *scriptHandlerObj = argv[0];
1321 Jim_CreateFileHandler(interp, af->fd, mask,
1322 JimAioFileEventHandler, scriptHandlerObj, JimAioFileEventFinalizer);
1324 return JIM_OK;
1327 static int aio_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1329 AioFile *af = Jim_CmdPrivData(interp);
1331 return aio_eventinfo(interp, af, JIM_EVENT_READABLE, &af->rEvent, argc, argv);
1334 static int aio_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1336 AioFile *af = Jim_CmdPrivData(interp);
1338 return aio_eventinfo(interp, af, JIM_EVENT_WRITABLE, &af->wEvent, argc, argv);
1341 static int aio_cmd_onexception(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1343 AioFile *af = Jim_CmdPrivData(interp);
1345 return aio_eventinfo(interp, af, JIM_EVENT_EXCEPTION, &af->eEvent, argc, argv);
1347 #endif
1349 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1350 static int aio_cmd_ssl(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1352 AioFile *af = Jim_CmdPrivData(interp);
1353 SSL *ssl;
1354 SSL_CTX *ssl_ctx;
1355 int server = 0;
1357 if (argc == 5) {
1358 if (!Jim_CompareStringImmediate(interp, argv[2], "-server")) {
1359 return JIM_ERR;
1361 server = 1;
1363 else if (argc != 2) {
1364 return -1;
1367 if (af->ssl) {
1368 Jim_SetResultFormatted(interp, "%#s: stream is already ssl", argv[0]);
1369 return JIM_ERR;
1372 ssl_ctx = JimAioSslCtx(interp);
1373 if (ssl_ctx == NULL) {
1374 return JIM_ERR;
1377 ssl = SSL_new(ssl_ctx);
1378 if (ssl == NULL) {
1379 goto out;
1382 SSL_set_cipher_list(ssl, "ALL");
1384 if (SSL_set_fd(ssl, fileno(af->fp)) == 0) {
1385 goto out;
1388 if (server) {
1389 if (SSL_use_certificate_file(ssl, Jim_String(argv[3]), SSL_FILETYPE_PEM) != 1) {
1390 goto out;
1393 if (SSL_use_PrivateKey_file(ssl, Jim_String(argv[4]), SSL_FILETYPE_PEM) != 1) {
1394 goto out;
1397 if (SSL_accept(ssl) != 1) {
1398 goto out;
1401 else {
1402 if (SSL_connect(ssl) != 1) {
1403 goto out;
1407 af->ssl = ssl;
1408 af->fops = &ssl_fops;
1410 /* Set the command name as the result */
1411 Jim_SetResult(interp, argv[0]);
1413 return JIM_OK;
1415 out:
1416 if (ssl) {
1417 SSL_free(ssl);
1419 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1420 return JIM_ERR;
1423 static int aio_cmd_verify(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1425 AioFile *af = Jim_CmdPrivData(interp);
1426 int ret;
1428 if (!af->fops->verify) {
1429 return JIM_OK;
1432 ret = af->fops->verify(af);
1433 if (ret != JIM_OK) {
1434 if (JimCheckStreamError(interp, af) == JIM_OK) {
1435 Jim_SetResultString(interp, "failed to verify the connection authenticity", -1);
1438 return ret;
1440 #endif /* JIM_BOOTSTRAP */
1442 #if defined(HAVE_STRUCT_FLOCK) && !defined(JIM_BOOTSTRAP)
1443 static int aio_cmd_lock(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1445 AioFile *af = Jim_CmdPrivData(interp);
1446 struct flock fl;
1447 int lockmode = F_SETLK;
1449 if (argc == 1) {
1450 if (!Jim_CompareStringImmediate(interp, argv[0], "-wait")) {
1451 return -1;
1453 lockmode = F_SETLKW;
1456 fl.l_start = 0;
1457 fl.l_len = 0;
1458 fl.l_type = F_WRLCK;
1459 fl.l_whence = SEEK_SET;
1461 switch (fcntl(af->fd, lockmode, &fl))
1463 case 0:
1464 Jim_SetResultInt(interp, 1);
1465 break;
1466 case -1:
1467 if (errno == EACCES || errno == EAGAIN)
1468 Jim_SetResultInt(interp, 0);
1469 else
1471 Jim_SetResultFormatted(interp, "lock failed: %s",
1472 strerror(errno));
1473 return JIM_ERR;
1475 break;
1476 default:
1477 Jim_SetResultInt(interp, 0);
1478 break;
1481 return JIM_OK;
1484 static int aio_cmd_unlock(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1486 AioFile *af = Jim_CmdPrivData(interp);
1487 struct flock fl;
1488 fl.l_start = 0;
1489 fl.l_len = 0;
1490 fl.l_type = F_UNLCK;
1491 fl.l_whence = SEEK_SET;
1493 Jim_SetResultInt(interp, fcntl(af->fd, F_SETLK, &fl) == 0);
1494 return JIM_OK;
1496 #endif /* JIM_BOOTSTRAP */
1498 #if defined(HAVE_TERMIOS_H) && !defined(JIM_BOOTSTRAP)
1499 static int aio_cmd_tty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1501 AioFile *af = Jim_CmdPrivData(interp);
1502 Jim_Obj *dictObjPtr;
1503 int ret;
1505 if (argc == 0) {
1506 /* get the current settings as a dictionary */
1507 dictObjPtr = Jim_GetTtySettings(interp, af->fd);
1508 if (dictObjPtr == NULL) {
1509 JimAioSetError(interp, NULL);
1510 return JIM_ERR;
1512 Jim_SetResult(interp, dictObjPtr);
1513 return JIM_OK;
1516 if (argc > 1) {
1517 /* Convert name value arguments to a dictionary */
1518 dictObjPtr = Jim_NewListObj(interp, argv, argc);
1520 else {
1521 /* The settings are already given as a list */
1522 dictObjPtr = argv[0];
1524 Jim_IncrRefCount(dictObjPtr);
1526 if (Jim_ListLength(interp, dictObjPtr) % 2) {
1527 /* Must be a valid dictionary */
1528 Jim_DecrRefCount(interp, dictObjPtr);
1529 return -1;
1532 ret = Jim_SetTtySettings(interp, af->fd, dictObjPtr);
1533 if (ret < 0) {
1534 JimAioSetError(interp, NULL);
1535 ret = JIM_ERR;
1537 Jim_DecrRefCount(interp, dictObjPtr);
1539 return ret;
1541 #endif /* JIM_BOOTSTRAP */
1543 static const jim_subcmd_type aio_command_table[] = {
1544 { "read",
1545 "?-nonewline? ?len?",
1546 aio_cmd_read,
1549 /* Description: Read and return bytes from the stream. To eof if no len. */
1551 { "copyto",
1552 "handle ?size?",
1553 aio_cmd_copy,
1556 /* Description: Copy up to 'size' bytes to the given filehandle, or to eof if no size. */
1558 { "getfd",
1559 NULL,
1560 aio_cmd_getfd,
1563 /* Description: Internal command to return the underlying file descriptor. */
1565 { "gets",
1566 "?var?",
1567 aio_cmd_gets,
1570 /* Description: Read one line and return it or store it in the var */
1572 { "puts",
1573 "?-nonewline? str",
1574 aio_cmd_puts,
1577 /* Description: Write the string, with newline unless -nonewline */
1579 { "isatty",
1580 NULL,
1581 aio_cmd_isatty,
1584 /* Description: Is the file descriptor a tty? */
1586 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
1587 { "recvfrom",
1588 "len ?addrvar?",
1589 aio_cmd_recvfrom,
1592 /* Description: Receive up to 'len' bytes on the socket. Sets 'addrvar' with receive address, if set */
1594 { "sendto",
1595 "str address",
1596 aio_cmd_sendto,
1599 /* Description: Send 'str' to the given address (dgram only) */
1601 { "accept",
1602 "?addrvar?",
1603 aio_cmd_accept,
1606 /* Description: Server socket only: Accept a connection and return stream */
1608 { "listen",
1609 "backlog",
1610 aio_cmd_listen,
1613 /* Description: Set the listen backlog for server socket */
1615 { "sockopt",
1616 "?opt 0|1?",
1617 aio_cmd_sockopt,
1620 /* Description: Return a dictionary of sockopts, or set the value of a sockopt */
1622 { "sockname",
1623 NULL,
1624 aio_cmd_sockname,
1627 /* Description: Returns the local address of the socket, if any */
1629 { "peername",
1630 NULL,
1631 aio_cmd_peername,
1634 /* Description: Returns the remote address of the socket, if any */
1636 #endif /* JIM_BOOTSTRAP */
1637 { "flush",
1638 NULL,
1639 aio_cmd_flush,
1642 /* Description: Flush the stream */
1644 { "eof",
1645 NULL,
1646 aio_cmd_eof,
1649 /* Description: Returns 1 if stream is at eof */
1651 { "close",
1652 "?r(ead)|w(rite)?",
1653 aio_cmd_close,
1656 JIM_MODFLAG_FULLARGV,
1657 /* Description: Closes the stream. */
1659 { "seek",
1660 "offset ?start|current|end",
1661 aio_cmd_seek,
1664 /* Description: Seeks in the stream (default 'current') */
1666 { "tell",
1667 NULL,
1668 aio_cmd_tell,
1671 /* Description: Returns the current seek position */
1673 { "filename",
1674 NULL,
1675 aio_cmd_filename,
1678 /* Description: Returns the original filename */
1680 #ifdef O_NDELAY
1681 { "ndelay",
1682 "?0|1?",
1683 aio_cmd_ndelay,
1686 /* Description: Set O_NDELAY (if arg). Returns current/new setting. */
1688 #endif
1689 #ifdef HAVE_FSYNC
1690 { "sync",
1691 NULL,
1692 aio_cmd_sync,
1695 /* Description: Flush and fsync() the stream */
1697 #endif
1698 { "buffering",
1699 "none|line|full",
1700 aio_cmd_buffering,
1703 /* Description: Sets buffering */
1705 #ifdef jim_ext_eventloop
1706 { "readable",
1707 "?readable-script?",
1708 aio_cmd_readable,
1711 /* Description: Returns script, or invoke readable-script when readable, {} to remove */
1713 { "writable",
1714 "?writable-script?",
1715 aio_cmd_writable,
1718 /* Description: Returns script, or invoke writable-script when writable, {} to remove */
1720 { "onexception",
1721 "?exception-script?",
1722 aio_cmd_onexception,
1725 /* Description: Returns script, or invoke exception-script when oob data, {} to remove */
1727 #endif
1728 #if !defined(JIM_BOOTSTRAP)
1729 #if defined(JIM_SSL)
1730 { "ssl",
1731 "?-server cert priv?",
1732 aio_cmd_ssl,
1735 JIM_MODFLAG_FULLARGV
1736 /* Description: Wraps a stream socket with SSL/TLS and returns a new channel */
1738 { "verify",
1739 NULL,
1740 aio_cmd_verify,
1743 /* Description: Verifies the certificate of a SSL/TLS channel */
1745 #endif
1746 #if defined(HAVE_STRUCT_FLOCK)
1747 { "lock ?-wait?",
1748 NULL,
1749 aio_cmd_lock,
1752 /* Description: Attempt to get a lock, possibly waiting */
1754 { "unlock",
1755 NULL,
1756 aio_cmd_unlock,
1759 /* Description: Relase a lock. */
1761 #endif
1762 #if defined(HAVE_TERMIOS_H)
1763 { "tty",
1764 "?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?",
1765 aio_cmd_tty,
1768 /* Description: Get or set tty settings - valid only on a tty */
1770 #endif
1771 #endif /* JIM_BOOTSTRAP */
1772 { NULL }
1775 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1777 return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, aio_command_table, argc, argv), argc, argv);
1780 static int JimAioOpenCommand(Jim_Interp *interp, int argc,
1781 Jim_Obj *const *argv)
1783 const char *mode;
1785 if (argc != 2 && argc != 3) {
1786 Jim_WrongNumArgs(interp, 1, argv, "filename ?mode?");
1787 return JIM_ERR;
1790 mode = (argc == 3) ? Jim_String(argv[2]) : "r";
1792 #ifdef jim_ext_tclcompat
1794 const char *filename = Jim_String(argv[1]);
1796 /* If the filename starts with '|', use popen instead */
1797 if (*filename == '|') {
1798 Jim_Obj *evalObj[3];
1800 evalObj[0] = Jim_NewStringObj(interp, "::popen", -1);
1801 evalObj[1] = Jim_NewStringObj(interp, filename + 1, -1);
1802 evalObj[2] = Jim_NewStringObj(interp, mode, -1);
1804 return Jim_EvalObjVector(interp, 3, evalObj);
1807 #endif
1808 return JimMakeChannel(interp, NULL, -1, argv[1], "aio.handle%ld", 0, mode, 0) ? JIM_OK : JIM_ERR;
1811 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1812 static void JimAioSslContextDelProc(struct Jim_Interp *interp, void *privData)
1814 SSL_CTX_free((SSL_CTX *)privData);
1815 ERR_free_strings();
1818 #ifdef USE_TLSv1_2_method
1819 #define TLS_method TLSv1_2_method
1820 #endif
1822 static SSL_CTX *JimAioSslCtx(Jim_Interp *interp)
1824 SSL_CTX *ssl_ctx = (SSL_CTX *)Jim_GetAssocData(interp, "ssl_ctx");
1825 if (ssl_ctx == NULL) {
1826 SSL_load_error_strings();
1827 SSL_library_init();
1828 ssl_ctx = SSL_CTX_new(TLS_method());
1829 if (ssl_ctx && SSL_CTX_set_default_verify_paths(ssl_ctx)) {
1830 SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
1831 Jim_SetAssocData(interp, "ssl_ctx", JimAioSslContextDelProc, ssl_ctx);
1832 } else {
1833 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1836 return ssl_ctx;
1838 #endif /* JIM_BOOTSTRAP */
1841 * Creates a channel for fh/fd/filename.
1843 * If fh is not NULL, uses that as the channel (and sets AIO_KEEPOPEN).
1844 * Otherwise, if fd is >= 0, uses that as the channel.
1845 * Otherwise opens 'filename' with mode 'mode'.
1847 * hdlfmt is a sprintf format for the filehandle. Anything with %ld at the end will do.
1848 * mode is used for open or fdopen.
1850 * Creates the command and sets the name as the current result.
1851 * Returns the AioFile pointer on sucess or NULL on failure.
1853 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
1854 const char *hdlfmt, int family, const char *mode, int flags)
1856 AioFile *af;
1857 char buf[AIO_CMD_LEN];
1859 snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
1860 if (!filename) {
1861 filename = Jim_NewStringObj(interp, buf, -1);
1864 Jim_IncrRefCount(filename);
1866 if (fh == NULL) {
1867 if (fd >= 0) {
1868 #ifndef JIM_ANSIC
1869 fh = fdopen(fd, mode);
1870 #endif
1872 else
1873 fh = fopen(Jim_String(filename), mode);
1875 if (fh == NULL) {
1876 JimAioSetError(interp, filename);
1877 #ifndef JIM_ANSIC
1878 if (fd >= 0) {
1879 close(fd);
1881 #endif
1882 Jim_DecrRefCount(interp, filename);
1883 return NULL;
1887 /* Create the file command */
1888 af = Jim_Alloc(sizeof(*af));
1889 memset(af, 0, sizeof(*af));
1890 af->fp = fh;
1891 af->filename = filename;
1892 af->openFlags = flags;
1893 #ifndef JIM_ANSIC
1894 af->fd = fileno(fh);
1895 #ifdef FD_CLOEXEC
1896 if ((flags & AIO_KEEPOPEN) == 0) {
1897 (void)fcntl(af->fd, F_SETFD, FD_CLOEXEC);
1899 #endif
1900 #endif
1901 af->addr_family = family;
1902 af->fops = &stdio_fops;
1903 af->ssl = NULL;
1905 Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc);
1907 /* Note that the command must use the global namespace, even if
1908 * the current namespace is something different
1910 Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, Jim_NewStringObj(interp, buf, -1)));
1912 return af;
1915 #if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && UNIX_SOCKETS)
1917 * Create a pair of channels. e.g. from pipe() or socketpair()
1919 static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename,
1920 const char *hdlfmt, int family, const char * const mode[2])
1922 if (JimMakeChannel(interp, NULL, p[0], filename, hdlfmt, family, mode[0], 0)) {
1923 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
1924 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
1925 if (JimMakeChannel(interp, NULL, p[1], filename, hdlfmt, family, mode[1], 0)) {
1926 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
1927 Jim_SetResult(interp, objPtr);
1928 return JIM_OK;
1932 /* Can only be here if fdopen() failed */
1933 close(p[0]);
1934 close(p[1]);
1935 JimAioSetError(interp, NULL);
1936 return JIM_ERR;
1938 #endif
1940 #ifdef HAVE_PIPE
1941 static int JimAioPipeCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1943 int p[2];
1944 static const char * const mode[2] = { "r", "w" };
1946 if (argc != 1) {
1947 Jim_WrongNumArgs(interp, 1, argv, "");
1948 return JIM_ERR;
1951 if (pipe(p) != 0) {
1952 JimAioSetError(interp, NULL);
1953 return JIM_ERR;
1956 return JimMakeChannelPair(interp, p, argv[0], "aio.pipe%ld", 0, mode);
1958 #endif
1960 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
1962 static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1964 const char *socktypes[] = {
1965 "unix",
1966 "unix.server",
1967 "unix.dgram",
1968 "unix.dgram.server",
1969 "dgram",
1970 "dgram.server",
1971 "stream",
1972 "stream.server",
1973 "pipe",
1974 "pair",
1975 NULL
1977 enum
1979 SOCK_UNIX,
1980 SOCK_UNIX_SERVER,
1981 SOCK_UNIX_DGRAM,
1982 SOCK_UNIX_DGRAM_SERVER,
1983 SOCK_DGRAM_CLIENT,
1984 SOCK_DGRAM_SERVER,
1985 SOCK_STREAM_CLIENT,
1986 SOCK_STREAM_SERVER,
1987 SOCK_STREAM_PIPE,
1988 SOCK_STREAM_SOCKETPAIR,
1990 int socktype;
1991 int sock;
1992 const char *addr = NULL;
1993 const char *bind_addr = NULL;
1994 const char *connect_addr = NULL;
1995 union sockaddr_any sa;
1996 socklen_t salen;
1997 int on = 1;
1998 int reuse = 0;
1999 int do_listen = 0;
2000 int family = PF_INET;
2001 int type = SOCK_STREAM;
2002 Jim_Obj *argv0 = argv[0];
2003 int ipv6 = 0;
2005 if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-ipv6")) {
2006 if (!IPV6) {
2007 Jim_SetResultString(interp, "ipv6 not supported", -1);
2008 return JIM_ERR;
2010 ipv6 = 1;
2011 family = PF_INET6;
2013 argc -= ipv6;
2014 argv += ipv6;
2016 if (argc < 2) {
2017 wrongargs:
2018 Jim_WrongNumArgs(interp, 1, &argv0, "?-ipv6? type ?address?");
2019 return JIM_ERR;
2022 if (Jim_GetEnum(interp, argv[1], socktypes, &socktype, "socket type", JIM_ERRMSG) != JIM_OK)
2023 return Jim_CheckShowCommands(interp, argv[1], socktypes);
2025 Jim_SetEmptyResult(interp);
2027 if (argc > 2) {
2028 addr = Jim_String(argv[2]);
2031 #if defined(HAVE_SOCKETPAIR) && UNIX_SOCKETS
2032 if (socktype == SOCK_STREAM_SOCKETPAIR) {
2033 int p[2];
2034 static const char * const mode[2] = { "r+", "r+" };
2036 if (addr || ipv6) {
2037 goto wrongargs;
2040 if (socketpair(PF_UNIX, SOCK_STREAM, 0, p) < 0) {
2041 JimAioSetError(interp, NULL);
2042 return JIM_ERR;
2044 return JimMakeChannelPair(interp, p, argv[1], "aio.sockpair%ld", PF_UNIX, mode);
2046 #endif
2048 #if defined(HAVE_PIPE)
2049 if (socktype == SOCK_STREAM_PIPE) {
2050 if (addr || ipv6) {
2051 goto wrongargs;
2053 return JimAioPipeCommand(interp, 1, &argv[1]);
2055 #endif
2057 /* Now all these socket types are very similar */
2058 switch (socktype) {
2059 case SOCK_DGRAM_CLIENT:
2060 connect_addr = addr;
2061 type = SOCK_DGRAM;
2062 break;
2064 case SOCK_STREAM_CLIENT:
2065 if (addr == NULL) {
2066 goto wrongargs;
2068 connect_addr = addr;
2069 break;
2071 case SOCK_STREAM_SERVER:
2072 if (addr == NULL) {
2073 goto wrongargs;
2075 bind_addr = addr;
2076 reuse = 1;
2077 do_listen = 1;
2078 break;
2080 case SOCK_DGRAM_SERVER:
2081 if (addr == NULL) {
2082 goto wrongargs;
2084 bind_addr = addr;
2085 type = SOCK_DGRAM;
2086 reuse = 1;
2087 break;
2089 #if UNIX_SOCKETS
2090 case SOCK_UNIX:
2091 if (addr == NULL) {
2092 goto wrongargs;
2094 connect_addr = addr;
2095 family = PF_UNIX;
2096 break;
2098 case SOCK_UNIX_DGRAM:
2099 connect_addr = addr;
2100 type = SOCK_DGRAM;
2101 family = PF_UNIX;
2102 /* A dgram unix domain socket client needs to bind
2103 * to a temporary address to allow the server to
2104 * send responses
2107 int tmpfd = Jim_MakeTempFile(interp, NULL, 1);
2108 if (tmpfd < 0) {
2109 return JIM_ERR;
2111 close(tmpfd);
2112 /* This will be valid until a result is next set, which is long enough here */
2113 bind_addr = Jim_String(Jim_GetResult(interp));
2115 break;
2117 case SOCK_UNIX_SERVER:
2118 if (addr == NULL) {
2119 goto wrongargs;
2121 bind_addr = addr;
2122 family = PF_UNIX;
2123 do_listen = 1;
2124 break;
2126 case SOCK_UNIX_DGRAM_SERVER:
2127 if (addr == NULL) {
2128 goto wrongargs;
2130 bind_addr = addr;
2131 type = SOCK_DGRAM;
2132 family = PF_UNIX;
2133 break;
2134 #endif
2136 default:
2137 Jim_SetResultString(interp, "Unsupported socket type", -1);
2138 return JIM_ERR;
2141 /* Now do all the steps necessary for the given socket type */
2142 sock = socket(family, type, 0);
2143 if (sock < 0) {
2144 JimAioSetError(interp, NULL);
2145 return JIM_ERR;
2147 if (bind_addr) {
2148 if (JimParseSocketAddress(interp, family, bind_addr, &sa, &salen) != JIM_OK) {
2149 close(sock);
2150 return JIM_ERR;
2152 if (reuse) {
2153 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
2155 if (bind(sock, &sa.sa, salen)) {
2156 Jim_SetResultFormatted(interp, "%s: bind: %s", bind_addr, strerror(errno));
2157 close(sock);
2158 return JIM_ERR;
2161 if (connect_addr) {
2162 if (JimParseSocketAddress(interp, family, connect_addr, &sa, &salen) != JIM_OK) {
2163 close(sock);
2164 return JIM_ERR;
2166 if (connect(sock, &sa.sa, salen)) {
2167 Jim_SetResultFormatted(interp, "%s: connect: %s", connect_addr, strerror(errno));
2168 close(sock);
2169 return JIM_ERR;
2172 if (do_listen) {
2173 if (listen(sock, 5)) {
2174 Jim_SetResultFormatted(interp, "listen: %s", strerror(errno));
2175 close(sock);
2176 return JIM_ERR;
2180 return JimMakeChannel(interp, NULL, sock, argv[1], "aio.sock%ld", family, "r+", 0) ? JIM_OK : JIM_ERR;
2182 #endif /* JIM_BOOTSTRAP */
2184 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
2185 static int JimAioLoadSSLCertsCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2187 SSL_CTX *ssl_ctx;
2189 if (argc != 2) {
2190 Jim_WrongNumArgs(interp, 1, argv, "dir");
2191 return JIM_ERR;
2194 ssl_ctx = JimAioSslCtx(interp);
2195 if (!ssl_ctx) {
2196 return JIM_ERR;
2198 if (SSL_CTX_load_verify_locations(ssl_ctx, NULL, Jim_String(argv[1])) == 1) {
2199 return JIM_OK;
2201 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
2202 return JIM_ERR;
2204 #endif /* JIM_BOOTSTRAP */
2206 int Jim_aioInit(Jim_Interp *interp)
2208 if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG))
2209 return JIM_ERR;
2211 #if defined(JIM_SSL)
2212 Jim_CreateCommand(interp, "load_ssl_certs", JimAioLoadSSLCertsCommand, NULL, NULL);
2213 #endif
2215 Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL);
2216 #ifdef HAVE_SOCKETS
2217 Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL);
2218 #endif
2219 #ifdef HAVE_PIPE
2220 Jim_CreateCommand(interp, "pipe", JimAioPipeCommand, NULL, NULL);
2221 #endif
2223 /* Create filehandles for stdin, stdout and stderr */
2224 JimMakeChannel(interp, stdin, -1, NULL, "stdin", 0, "r", AIO_KEEPOPEN);
2225 JimMakeChannel(interp, stdout, -1, NULL, "stdout", 0, "w", AIO_KEEPOPEN);
2226 JimMakeChannel(interp, stderr, -1, NULL, "stderr", 0, "w", AIO_KEEPOPEN);
2228 return JIM_OK;