Fix C++ compatibility
[jimtcl.git] / jim-aio.c
blobb9eb441d1f143a30a246a0719bf64a97ca91141e
1 /* Jim - A small embeddable Tcl interpreter
3 * Copyright 2005 Salvatore Sanfilippo <antirez@invece.org>
4 * Copyright 2005 Clemens Hintze <c.hintze@gmx.net>
5 * Copyright 2005 patthoyts - Pat Thoyts <patthoyts@users.sf.net>
6 * Copyright 2008 oharboe - Øyvind Harboe - oyvind.harboe@zylin.com
7 * Copyright 2008 Andrew Lunn <andrew@lunn.ch>
8 * Copyright 2008 Duane Ellis <openocd@duaneellis.com>
9 * Copyright 2008 Uwe Klein <uklein@klein-messgeraete.de>
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above
18 * copyright notice, this list of conditions and the following
19 * disclaimer in the documentation and/or other materials
20 * provided with the distribution.
22 * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY
23 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
24 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
25 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
33 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 * The views and conclusions contained in the software and documentation
36 * are those of the authors and should not be interpreted as representing
37 * official policies, either expressed or implied, of the Jim Tcl Project.
38 **/
40 #include "jimautoconf.h"
42 #ifndef _GNU_SOURCE
43 #define _GNU_SOURCE
44 #endif
45 #include <stdio.h>
46 #include <string.h>
47 #include <errno.h>
48 #include <fcntl.h>
49 #ifdef HAVE_UNISTD_H
50 #include <unistd.h>
51 #include <sys/stat.h>
52 #endif
54 #include "jim.h"
56 #if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H)
57 #include <sys/socket.h>
58 #include <netinet/in.h>
59 #include <arpa/inet.h>
60 #include <netdb.h>
61 #ifdef HAVE_SYS_UN_H
62 #include <sys/un.h>
63 #endif
64 #else
65 #define JIM_ANSIC
66 #endif
68 #if defined(JIM_SSL)
69 #include <openssl/ssl.h>
70 #include <openssl/err.h>
71 #endif
73 #ifdef HAVE_TERMIOS_H
74 #include <jim-tty.h>
75 #endif
77 #include "jim-eventloop.h"
78 #include "jim-subcmd.h"
80 #define AIO_CMD_LEN 32 /* e.g. aio.handleXXXXXX */
81 #define AIO_BUF_LEN 256 /* Can keep this small and rely on stdio buffering */
83 #ifndef HAVE_FTELLO
84 #define ftello ftell
85 #endif
86 #ifndef HAVE_FSEEKO
87 #define fseeko fseek
88 #endif
90 #define AIO_KEEPOPEN 1
92 #if defined(JIM_IPV6)
93 #define IPV6 1
94 #else
95 #define IPV6 0
96 #ifndef PF_INET6
97 #define PF_INET6 0
98 #endif
99 #endif
101 #define JimCheckStreamError(interp, af) af->fops->error(af)
103 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
104 union sockaddr_any {
105 struct sockaddr sa;
106 struct sockaddr_in sin;
107 #if IPV6
108 struct sockaddr_in6 sin6;
109 #endif
112 #ifndef HAVE_INET_NTOP
113 const char *inet_ntop(int af, const void *src, char *dst, int size)
115 if (af != PF_INET) {
116 return NULL;
118 snprintf(dst, size, "%s", inet_ntoa(((struct sockaddr_in *)src)->sin_addr));
119 return dst;
121 #endif
122 #endif /* JIM_BOOTSTRAP */
124 struct AioFile;
126 typedef struct {
127 int (*writer)(struct AioFile *af, const char *buf, int len);
128 int (*reader)(struct AioFile *af, char *buf, int len);
129 const char *(*getline)(struct AioFile *af, char *buf, int len);
130 int (*error)(const struct AioFile *af);
131 const char *(*strerror)(struct AioFile *af);
132 int (*verify)(struct AioFile *af);
133 } JimAioFopsType;
135 typedef struct AioFile
137 FILE *fp;
138 Jim_Obj *filename;
139 int type;
140 int openFlags; /* AIO_KEEPOPEN? keep FILE* */
141 int fd;
142 Jim_Obj *rEvent;
143 Jim_Obj *wEvent;
144 Jim_Obj *eEvent;
145 int addr_family;
146 void *ssl;
147 const JimAioFopsType *fops;
148 } AioFile;
150 static int stdio_writer(struct AioFile *af, const char *buf, int len)
152 return fwrite(buf, 1, len, af->fp);
155 static int stdio_reader(struct AioFile *af, char *buf, int len)
157 return fread(buf, 1, len, af->fp);
160 static const char *stdio_getline(struct AioFile *af, char *buf, int len)
162 return fgets(buf, len, af->fp);
165 static int stdio_error(const AioFile *af)
167 if (!ferror(af->fp)) {
168 return JIM_OK;
170 clearerr(af->fp);
171 /* EAGAIN and similar are not error conditions. Just treat them like eof */
172 if (feof(af->fp) || errno == EAGAIN || errno == EINTR) {
173 return JIM_OK;
175 #ifdef ECONNRESET
176 if (errno == ECONNRESET) {
177 return JIM_OK;
179 #endif
180 #ifdef ECONNABORTED
181 if (errno != ECONNABORTED) {
182 return JIM_OK;
184 #endif
185 return JIM_ERR;
188 static const char *stdio_strerror(struct AioFile *af)
190 return strerror(errno);
193 static const JimAioFopsType stdio_fops = {
194 stdio_writer,
195 stdio_reader,
196 stdio_getline,
197 stdio_error,
198 stdio_strerror,
199 NULL
202 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
204 static SSL_CTX *JimAioSslCtx(Jim_Interp *interp);
206 static int ssl_writer(struct AioFile *af, const char *buf, int len)
208 return SSL_write(af->ssl, buf, len);
211 static int ssl_reader(struct AioFile *af, char *buf, int len)
213 return SSL_read(af->ssl, buf, len);
216 static const char *ssl_getline(struct AioFile *af, char *buf, int len)
218 int i;
219 for (i = 0; i < len + 1; i++) {
220 if (SSL_read(af->ssl, &buf[i], 1) != 1) {
221 if (i == 0) {
222 return NULL;
224 break;
226 if (buf[i] == '\n') {
227 break;
230 buf[i] = '\0';
231 return buf;
234 static int ssl_error(const struct AioFile *af)
236 if (ERR_peek_error() == 0) {
237 return JIM_OK;
240 return JIM_ERR;
243 static const char *ssl_strerror(struct AioFile *af)
245 int err = ERR_get_error();
247 if (err) {
248 return ERR_error_string(err, NULL);
251 /* should not happen */
252 return "unknown SSL error";
255 static int ssl_verify(struct AioFile *af)
257 X509 *cert;
259 cert = SSL_get_peer_certificate(af->ssl);
260 if (!cert) {
261 return JIM_ERR;
263 X509_free(cert);
265 if (SSL_get_verify_result(af->ssl) == X509_V_OK) {
266 return JIM_OK;
269 return JIM_ERR;
272 static const JimAioFopsType ssl_fops = {
273 ssl_writer,
274 ssl_reader,
275 ssl_getline,
276 ssl_error,
277 ssl_strerror,
278 ssl_verify
280 #endif /* JIM_BOOTSTRAP */
282 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
283 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
284 const char *hdlfmt, int family, const char *mode);
286 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
287 static int JimParseIPv6Address(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, int *salen)
289 #if IPV6
291 * An IPv6 addr/port looks like:
292 * [::1]
293 * [::1]:2000
294 * [fe80::223:6cff:fe95:bdc0%en1]:2000
295 * [::]:2000
296 * 2000
298 * Note that the "any" address is ::, which is the same as when no address is specified.
300 char *sthost = NULL;
301 const char *stport;
302 int ret = JIM_OK;
303 struct addrinfo req;
304 struct addrinfo *ai;
306 stport = strrchr(hostport, ':');
307 if (!stport) {
308 /* No : so, the whole thing is the port */
309 stport = hostport;
310 hostport = "::";
311 sthost = Jim_StrDup(hostport);
313 else {
314 stport++;
317 if (*hostport == '[') {
318 /* This is a numeric ipv6 address */
319 char *pt = strchr(++hostport, ']');
320 if (pt) {
321 sthost = Jim_StrDupLen(hostport, pt - hostport);
325 if (!sthost) {
326 sthost = Jim_StrDupLen(hostport, stport - hostport - 1);
329 memset(&req, '\0', sizeof(req));
330 req.ai_family = PF_INET6;
332 if (getaddrinfo(sthost, NULL, &req, &ai)) {
333 Jim_SetResultFormatted(interp, "Not a valid address: %s", hostport);
334 ret = JIM_ERR;
336 else {
337 memcpy(&sa->sin, ai->ai_addr, ai->ai_addrlen);
338 *salen = ai->ai_addrlen;
340 sa->sin.sin_port = htons(atoi(stport));
342 freeaddrinfo(ai);
344 Jim_Free(sthost);
346 return ret;
347 #else
348 Jim_SetResultString(interp, "ipv6 not supported", -1);
349 return JIM_ERR;
350 #endif
353 static int JimParseIpAddress(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, int *salen)
355 /* An IPv4 addr/port looks like:
356 * 192.168.1.5
357 * 192.168.1.5:2000
358 * 2000
360 * If the address is missing, INADDR_ANY is used.
361 * If the port is missing, 0 is used (only useful for server sockets).
363 char *sthost = NULL;
364 const char *stport;
365 int ret = JIM_OK;
367 stport = strrchr(hostport, ':');
368 if (!stport) {
369 /* No : so, the whole thing is the port */
370 stport = hostport;
371 sthost = Jim_StrDup("0.0.0.0");
373 else {
374 sthost = Jim_StrDupLen(hostport, stport - hostport);
375 stport++;
379 #ifdef HAVE_GETADDRINFO
380 struct addrinfo req;
381 struct addrinfo *ai;
382 memset(&req, '\0', sizeof(req));
383 req.ai_family = PF_INET;
385 if (getaddrinfo(sthost, NULL, &req, &ai)) {
386 ret = JIM_ERR;
388 else {
389 memcpy(&sa->sin, ai->ai_addr, ai->ai_addrlen);
390 *salen = ai->ai_addrlen;
391 freeaddrinfo(ai);
393 #else
394 struct hostent *he;
396 ret = JIM_ERR;
398 if ((he = gethostbyname(sthost)) != NULL) {
399 if (he->h_length == sizeof(sa->sin.sin_addr)) {
400 *salen = sizeof(sa->sin);
401 sa->sin.sin_family= he->h_addrtype;
402 memcpy(&sa->sin.sin_addr, he->h_addr, he->h_length); /* set address */
403 ret = JIM_OK;
406 #endif
408 sa->sin.sin_port = htons(atoi(stport));
410 Jim_Free(sthost);
412 if (ret != JIM_OK) {
413 Jim_SetResultFormatted(interp, "Not a valid address: %s", hostport);
416 return ret;
419 #ifdef HAVE_SYS_UN_H
420 static int JimParseDomainAddress(Jim_Interp *interp, const char *path, struct sockaddr_un *sa)
422 sa->sun_family = PF_UNIX;
423 snprintf(sa->sun_path, sizeof(sa->sun_path), "%s", path);
425 return JIM_OK;
427 #endif
430 * Format that address in 'sa' as a string and store in variable 'varObjPtr'
432 static int JimFormatIpAddress(Jim_Interp *interp, Jim_Obj *varObjPtr, const union sockaddr_any *sa)
434 /* INET6_ADDRSTRLEN is 46. Add some for [] and port */
435 char addrbuf[60];
437 #if IPV6
438 if (sa->sa.sa_family == PF_INET6) {
439 addrbuf[0] = '[';
440 /* Allow 9 for []:65535\0 */
441 inet_ntop(sa->sa.sa_family, &sa->sin6.sin6_addr, addrbuf + 1, sizeof(addrbuf) - 9);
442 snprintf(addrbuf + strlen(addrbuf), 8, "]:%d", ntohs(sa->sin.sin_port));
444 else
445 #endif
446 if (sa->sa.sa_family == PF_INET) {
447 /* Allow 7 for :65535\0 */
448 inet_ntop(sa->sa.sa_family, &sa->sin.sin_addr, addrbuf, sizeof(addrbuf) - 7);
449 snprintf(addrbuf + strlen(addrbuf), 7, ":%d", ntohs(sa->sin.sin_port));
451 else {
452 /* recvfrom still works on unix domain sockets, etc */
453 addrbuf[0] = 0;
456 return Jim_SetVariable(interp, varObjPtr, Jim_NewStringObj(interp, addrbuf, -1));
459 #endif /* JIM_BOOTSTRAP */
461 static const char *JimAioErrorString(AioFile *af)
463 if (af && af->fops)
464 return af->fops->strerror(af);
466 return strerror(errno);
469 static void JimAioSetError(Jim_Interp *interp, Jim_Obj *name)
471 AioFile *af = Jim_CmdPrivData(interp);
473 if (name) {
474 Jim_SetResultFormatted(interp, "%#s: %s", name, JimAioErrorString(af));
476 else {
477 Jim_SetResultString(interp, JimAioErrorString(af), -1);
481 static void JimAioDelProc(Jim_Interp *interp, void *privData)
483 AioFile *af = privData;
485 JIM_NOTUSED(interp);
487 Jim_DecrRefCount(interp, af->filename);
489 #ifdef jim_ext_eventloop
490 /* remove all existing EventHandlers */
491 Jim_DeleteFileHandler(interp, af->fd, JIM_EVENT_READABLE | JIM_EVENT_WRITABLE | JIM_EVENT_EXCEPTION);
492 #endif
494 #if defined(JIM_SSL)
495 if (af->ssl != NULL) {
496 SSL_free(af->ssl);
498 #endif
499 if (!(af->openFlags & AIO_KEEPOPEN)) {
500 fclose(af->fp);
503 Jim_Free(af);
506 static int aio_cmd_read(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
508 AioFile *af = Jim_CmdPrivData(interp);
509 char buf[AIO_BUF_LEN];
510 Jim_Obj *objPtr;
511 int nonewline = 0;
512 jim_wide neededLen = -1; /* -1 is "read as much as possible" */
514 if (argc && Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
515 nonewline = 1;
516 argv++;
517 argc--;
519 if (argc == 1) {
520 if (Jim_GetWide(interp, argv[0], &neededLen) != JIM_OK)
521 return JIM_ERR;
522 if (neededLen < 0) {
523 Jim_SetResultString(interp, "invalid parameter: negative len", -1);
524 return JIM_ERR;
527 else if (argc) {
528 return -1;
530 objPtr = Jim_NewStringObj(interp, NULL, 0);
531 while (neededLen != 0) {
532 int retval;
533 int readlen;
535 if (neededLen == -1) {
536 readlen = AIO_BUF_LEN;
538 else {
539 readlen = (neededLen > AIO_BUF_LEN ? AIO_BUF_LEN : neededLen);
541 retval = af->fops->reader(af, buf, readlen);
542 if (retval > 0) {
543 Jim_AppendString(interp, objPtr, buf, retval);
544 if (neededLen != -1) {
545 neededLen -= retval;
548 if (retval != readlen)
549 break;
551 /* Check for error conditions */
552 if (JimCheckStreamError(interp, af)) {
553 Jim_FreeNewObj(interp, objPtr);
554 return JIM_ERR;
556 if (nonewline) {
557 int len;
558 const char *s = Jim_GetString(objPtr, &len);
560 if (len > 0 && s[len - 1] == '\n') {
561 objPtr->length--;
562 objPtr->bytes[objPtr->length] = '\0';
565 Jim_SetResult(interp, objPtr);
566 return JIM_OK;
569 AioFile *Jim_AioFile(Jim_Interp *interp, Jim_Obj *command)
571 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, command, JIM_ERRMSG);
573 /* XXX: There ought to be a supported API for this */
574 if (cmdPtr && !cmdPtr->isproc && cmdPtr->u.native.cmdProc == JimAioSubCmdProc) {
575 return (AioFile *) cmdPtr->u.native.privData;
577 Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", command);
578 return NULL;
581 FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command)
583 AioFile *af;
585 af = Jim_AioFile(interp, command);
586 if (af == NULL) {
587 return NULL;
590 return af->fp;
593 static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
595 AioFile *af = Jim_CmdPrivData(interp);
596 jim_wide count = 0;
597 jim_wide maxlen = JIM_WIDE_MAX;
598 AioFile *outf = Jim_AioFile(interp, argv[0]);
600 if (outf == NULL) {
601 return JIM_ERR;
604 if (argc == 2) {
605 if (Jim_GetWide(interp, argv[1], &maxlen) != JIM_OK) {
606 return JIM_ERR;
610 while (count < maxlen) {
611 char ch;
613 if (af->fops->reader(af, &ch, 1) != 1) {
614 break;
616 if (outf->fops->writer(outf, &ch, 1) != 1) {
617 break;
619 count++;
622 if (JimCheckStreamError(interp, af) || JimCheckStreamError(interp, outf)) {
623 return JIM_ERR;
626 Jim_SetResultInt(interp, count);
628 return JIM_OK;
631 static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
633 AioFile *af = Jim_CmdPrivData(interp);
634 char buf[AIO_BUF_LEN];
635 Jim_Obj *objPtr;
636 int len;
638 errno = 0;
640 objPtr = Jim_NewStringObj(interp, NULL, 0);
641 while (1) {
642 buf[AIO_BUF_LEN - 1] = '_';
644 if (af->fops->getline(af, buf, AIO_BUF_LEN) == NULL)
645 break;
647 if (buf[AIO_BUF_LEN - 1] == '\0' && buf[AIO_BUF_LEN - 2] != '\n') {
648 Jim_AppendString(interp, objPtr, buf, AIO_BUF_LEN - 1);
650 else {
651 len = strlen(buf);
653 if (len && (buf[len - 1] == '\n')) {
654 /* strip "\n" */
655 len--;
658 Jim_AppendString(interp, objPtr, buf, len);
659 break;
663 if (JimCheckStreamError(interp, af)) {
664 /* I/O error */
665 Jim_FreeNewObj(interp, objPtr);
666 return JIM_ERR;
669 if (argc) {
670 if (Jim_SetVariable(interp, argv[0], objPtr) != JIM_OK) {
671 Jim_FreeNewObj(interp, objPtr);
672 return JIM_ERR;
675 len = Jim_Length(objPtr);
677 if (len == 0 && feof(af->fp)) {
678 /* On EOF returns -1 if varName was specified */
679 len = -1;
681 Jim_SetResultInt(interp, len);
683 else {
684 Jim_SetResult(interp, objPtr);
686 return JIM_OK;
689 static int aio_cmd_puts(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
691 AioFile *af = Jim_CmdPrivData(interp);
692 int wlen;
693 const char *wdata;
694 Jim_Obj *strObj;
696 if (argc == 2) {
697 if (!Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
698 return -1;
700 strObj = argv[1];
702 else {
703 strObj = argv[0];
706 wdata = Jim_GetString(strObj, &wlen);
707 if (af->fops->writer(af, wdata, wlen) == wlen) {
708 if (argc == 2 || af->fops->writer(af, "\n", 1) == 1) {
709 return JIM_OK;
712 JimAioSetError(interp, af->filename);
713 return JIM_ERR;
716 static int aio_cmd_isatty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
718 #ifdef HAVE_ISATTY
719 AioFile *af = Jim_CmdPrivData(interp);
720 Jim_SetResultInt(interp, isatty(fileno(af->fp)));
721 #else
722 Jim_SetResultInt(interp, 0);
723 #endif
725 return JIM_OK;
728 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
729 static int aio_cmd_recvfrom(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
731 AioFile *af = Jim_CmdPrivData(interp);
732 char *buf;
733 union sockaddr_any sa;
734 long len;
735 socklen_t salen = sizeof(sa);
736 int rlen;
738 if (Jim_GetLong(interp, argv[0], &len) != JIM_OK) {
739 return JIM_ERR;
742 buf = Jim_Alloc(len + 1);
744 rlen = recvfrom(fileno(af->fp), buf, len, 0, &sa.sa, &salen);
745 if (rlen < 0) {
746 Jim_Free(buf);
747 JimAioSetError(interp, NULL);
748 return JIM_ERR;
750 buf[rlen] = 0;
751 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, rlen));
753 if (argc > 1) {
754 return JimFormatIpAddress(interp, argv[1], &sa);
757 return JIM_OK;
761 static int aio_cmd_sendto(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
763 AioFile *af = Jim_CmdPrivData(interp);
764 int wlen;
765 int len;
766 const char *wdata;
767 union sockaddr_any sa;
768 const char *addr = Jim_String(argv[1]);
769 int salen;
771 if (IPV6 && af->addr_family == PF_INET6) {
772 if (JimParseIPv6Address(interp, addr, &sa, &salen) != JIM_OK) {
773 return JIM_ERR;
776 else if (JimParseIpAddress(interp, addr, &sa, &salen) != JIM_OK) {
777 return JIM_ERR;
779 wdata = Jim_GetString(argv[0], &wlen);
781 /* Note that we don't validate the socket type. Rely on sendto() failing if appropriate */
782 len = sendto(fileno(af->fp), wdata, wlen, 0, &sa.sa, salen);
783 if (len < 0) {
784 JimAioSetError(interp, NULL);
785 return JIM_ERR;
787 Jim_SetResultInt(interp, len);
788 return JIM_OK;
791 static int aio_cmd_accept(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
793 AioFile *af = Jim_CmdPrivData(interp);
794 int sock;
795 union sockaddr_any sa;
796 socklen_t addrlen = sizeof(sa);
798 sock = accept(af->fd, &sa.sa, &addrlen);
799 if (sock < 0) {
800 JimAioSetError(interp, NULL);
801 return JIM_ERR;
804 if (argc > 0) {
805 if (JimFormatIpAddress(interp, argv[0], &sa) != JIM_OK) {
806 return JIM_ERR;
810 /* Create the file command */
811 return JimMakeChannel(interp, NULL, sock, Jim_NewStringObj(interp, "accept", -1),
812 "aio.sockstream%ld", af->addr_family, "r+") ? JIM_OK : JIM_ERR;
815 static int aio_cmd_listen(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
817 AioFile *af = Jim_CmdPrivData(interp);
818 long backlog;
820 if (Jim_GetLong(interp, argv[0], &backlog) != JIM_OK) {
821 return JIM_ERR;
824 if (listen(af->fd, backlog)) {
825 JimAioSetError(interp, NULL);
826 return JIM_ERR;
829 return JIM_OK;
831 #endif /* JIM_BOOTSTRAP */
833 static int aio_cmd_flush(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
835 AioFile *af = Jim_CmdPrivData(interp);
837 if (fflush(af->fp) == EOF) {
838 JimAioSetError(interp, af->filename);
839 return JIM_ERR;
841 return JIM_OK;
844 static int aio_cmd_eof(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
846 AioFile *af = Jim_CmdPrivData(interp);
848 Jim_SetResultInt(interp, feof(af->fp));
849 return JIM_OK;
852 static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
854 if (argc == 3) {
855 #if !defined(JIM_ANSIC) && defined(HAVE_SHUTDOWN)
856 static const char * const options[] = { "r", "w", NULL };
857 enum { OPT_R, OPT_W, };
858 int option;
859 AioFile *af = Jim_CmdPrivData(interp);
861 if (Jim_GetEnum(interp, argv[2], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
862 return JIM_ERR;
864 if (shutdown(af->fd, option == OPT_R ? SHUT_RD : SHUT_WR) == 0) {
865 return JIM_OK;
867 JimAioSetError(interp, NULL);
868 #else
869 Jim_SetResultString(interp, "async close not supported", -1);
870 #endif
871 return JIM_ERR;
874 return Jim_DeleteCommand(interp, Jim_String(argv[0]));
877 static int aio_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
879 AioFile *af = Jim_CmdPrivData(interp);
880 int orig = SEEK_SET;
881 jim_wide offset;
883 if (argc == 2) {
884 if (Jim_CompareStringImmediate(interp, argv[1], "start"))
885 orig = SEEK_SET;
886 else if (Jim_CompareStringImmediate(interp, argv[1], "current"))
887 orig = SEEK_CUR;
888 else if (Jim_CompareStringImmediate(interp, argv[1], "end"))
889 orig = SEEK_END;
890 else {
891 return -1;
894 if (Jim_GetWide(interp, argv[0], &offset) != JIM_OK) {
895 return JIM_ERR;
897 if (fseeko(af->fp, offset, orig) == -1) {
898 JimAioSetError(interp, af->filename);
899 return JIM_ERR;
901 return JIM_OK;
904 static int aio_cmd_tell(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
906 AioFile *af = Jim_CmdPrivData(interp);
908 Jim_SetResultInt(interp, ftello(af->fp));
909 return JIM_OK;
912 static int aio_cmd_filename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
914 AioFile *af = Jim_CmdPrivData(interp);
916 Jim_SetResult(interp, af->filename);
917 return JIM_OK;
920 #ifdef O_NDELAY
921 static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
923 AioFile *af = Jim_CmdPrivData(interp);
925 int fmode = fcntl(af->fd, F_GETFL);
927 if (argc) {
928 long nb;
930 if (Jim_GetLong(interp, argv[0], &nb) != JIM_OK) {
931 return JIM_ERR;
933 if (nb) {
934 fmode |= O_NDELAY;
936 else {
937 fmode &= ~O_NDELAY;
939 (void)fcntl(af->fd, F_SETFL, fmode);
941 Jim_SetResultInt(interp, (fmode & O_NONBLOCK) ? 1 : 0);
942 return JIM_OK;
944 #endif
946 #ifdef HAVE_FSYNC
947 static int aio_cmd_sync(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
949 AioFile *af = Jim_CmdPrivData(interp);
951 fflush(af->fp);
952 fsync(af->fd);
953 return JIM_OK;
955 #endif
957 static int aio_cmd_buffering(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
959 AioFile *af = Jim_CmdPrivData(interp);
961 static const char * const options[] = {
962 "none",
963 "line",
964 "full",
965 NULL
967 enum
969 OPT_NONE,
970 OPT_LINE,
971 OPT_FULL,
973 int option;
975 if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
976 return JIM_ERR;
978 switch (option) {
979 case OPT_NONE:
980 setvbuf(af->fp, NULL, _IONBF, 0);
981 break;
982 case OPT_LINE:
983 setvbuf(af->fp, NULL, _IOLBF, BUFSIZ);
984 break;
985 case OPT_FULL:
986 setvbuf(af->fp, NULL, _IOFBF, BUFSIZ);
987 break;
989 return JIM_OK;
992 #ifdef jim_ext_eventloop
993 static void JimAioFileEventFinalizer(Jim_Interp *interp, void *clientData)
995 Jim_Obj **objPtrPtr = clientData;
997 Jim_DecrRefCount(interp, *objPtrPtr);
998 *objPtrPtr = NULL;
1001 static int JimAioFileEventHandler(Jim_Interp *interp, void *clientData, int mask)
1003 Jim_Obj **objPtrPtr = clientData;
1005 return Jim_EvalObjBackground(interp, *objPtrPtr);
1008 static int aio_eventinfo(Jim_Interp *interp, AioFile * af, unsigned mask, Jim_Obj **scriptHandlerObj,
1009 int argc, Jim_Obj * const *argv)
1011 if (argc == 0) {
1012 /* Return current script */
1013 if (*scriptHandlerObj) {
1014 Jim_SetResult(interp, *scriptHandlerObj);
1016 return JIM_OK;
1019 if (*scriptHandlerObj) {
1020 /* Delete old handler */
1021 Jim_DeleteFileHandler(interp, af->fd, mask);
1024 /* Now possibly add the new script(s) */
1025 if (Jim_Length(argv[0]) == 0) {
1026 /* Empty script, so done */
1027 return JIM_OK;
1030 /* A new script to add */
1031 Jim_IncrRefCount(argv[0]);
1032 *scriptHandlerObj = argv[0];
1034 Jim_CreateFileHandler(interp, af->fd, mask,
1035 JimAioFileEventHandler, scriptHandlerObj, JimAioFileEventFinalizer);
1037 return JIM_OK;
1040 static int aio_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1042 AioFile *af = Jim_CmdPrivData(interp);
1044 return aio_eventinfo(interp, af, JIM_EVENT_READABLE, &af->rEvent, argc, argv);
1047 static int aio_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1049 AioFile *af = Jim_CmdPrivData(interp);
1051 return aio_eventinfo(interp, af, JIM_EVENT_WRITABLE, &af->wEvent, argc, argv);
1054 static int aio_cmd_onexception(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1056 AioFile *af = Jim_CmdPrivData(interp);
1058 return aio_eventinfo(interp, af, JIM_EVENT_EXCEPTION, &af->eEvent, argc, argv);
1060 #endif
1062 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1063 static int aio_cmd_ssl(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1065 AioFile *af = Jim_CmdPrivData(interp);
1066 SSL *ssl;
1067 SSL_CTX *ssl_ctx;
1068 int fd, server = 0;
1070 if (argc == 5) {
1071 if (!Jim_CompareStringImmediate(interp, argv[2], "-server")) {
1072 return JIM_ERR;
1074 server = 1;
1076 else if (argc != 2) {
1077 Jim_WrongNumArgs(interp, 2, argv, "?-server cert priv?");
1078 return JIM_ERR;
1081 fd = fileno(af->fp);
1082 #if defined(HAVE_DUP)
1083 fd = dup(fd);
1084 if (fd < 0) {
1085 return JIM_ERR;
1087 #endif
1088 ssl_ctx = JimAioSslCtx(interp);
1089 if (ssl_ctx == NULL) {
1090 return JIM_ERR;
1093 ssl = SSL_new(ssl_ctx);
1094 if (ssl == NULL) {
1095 #if defined(HAVE_DUP)
1096 close(fd);
1097 #endif
1098 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1099 return JIM_ERR;
1102 SSL_set_cipher_list(ssl, "ALL");
1104 if (SSL_set_fd(ssl, fileno(af->fp)) == 0) {
1105 goto out;
1108 if (server) {
1109 if (SSL_use_certificate_file(ssl, Jim_String(argv[3]), SSL_FILETYPE_PEM) != 1) {
1110 goto out;
1113 if (SSL_use_PrivateKey_file(ssl, Jim_String(argv[4]), SSL_FILETYPE_PEM) != 1) {
1114 goto out;
1117 if (SSL_accept(ssl) != 1) {
1118 goto out;
1121 else {
1122 if (SSL_connect(ssl) != 1) {
1123 goto out;
1127 af = JimMakeChannel(interp, NULL, fd, NULL, "aio.sslstream%ld", af->addr_family, "r+");
1128 if (af == NULL) {
1129 goto out;
1132 af->ssl = ssl;
1133 af->fops = &ssl_fops;
1135 return JIM_OK;
1137 out:
1138 #if defined(HAVE_DUP)
1139 close(fd);
1140 #endif
1141 SSL_free(ssl);
1142 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1143 return JIM_ERR;
1146 static int aio_cmd_verify(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1148 AioFile *af = Jim_CmdPrivData(interp);
1149 int ret;
1151 if (!af->fops->verify) {
1152 return JIM_OK;
1155 ret = af->fops->verify(af);
1156 if (ret != JIM_OK) {
1157 if (JimCheckStreamError(interp, af)) {
1158 JimAioSetError(interp, af->filename);
1159 } else {
1160 Jim_SetResultString(interp, "failed to verify the connection authenticity", -1);
1163 return ret;
1165 #endif /* JIM_BOOTSTRAP */
1167 #if defined(HAVE_STRUCT_FLOCK) && !defined(JIM_BOOTSTRAP)
1168 static int aio_cmd_lock(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1170 AioFile *af = Jim_CmdPrivData(interp);
1171 struct flock fl;
1173 fl.l_start = 0;
1174 fl.l_len = 0;
1175 fl.l_type = F_WRLCK;
1176 fl.l_whence = SEEK_SET;
1178 switch (fcntl(af->fd, F_SETLK, &fl))
1180 case 0:
1181 Jim_SetResultInt(interp, 1);
1182 break;
1183 case -1:
1184 if (errno == EACCES || errno == EAGAIN)
1185 Jim_SetResultInt(interp, 0);
1186 else
1188 Jim_SetResultFormatted(interp, "lock failed: %s",
1189 strerror(errno));
1190 return JIM_ERR;
1192 break;
1193 default:
1194 Jim_SetResultInt(interp, 0);
1195 break;
1198 return JIM_OK;
1201 static int aio_cmd_unlock(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1203 AioFile *af = Jim_CmdPrivData(interp);
1204 struct flock fl;
1205 fl.l_start = 0;
1206 fl.l_len = 0;
1207 fl.l_type = F_UNLCK;
1208 fl.l_whence = SEEK_SET;
1210 Jim_SetResultInt(interp, fcntl(af->fd, F_SETLK, &fl) == 0);
1211 return JIM_OK;
1213 #endif /* JIM_BOOTSTRAP */
1215 #if defined(HAVE_TERMIOS_H) && !defined(JIM_BOOTSTRAP)
1216 static int aio_cmd_tty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1218 AioFile *af = Jim_CmdPrivData(interp);
1219 Jim_Obj *dictObjPtr;
1220 int ret;
1222 if (argc == 0) {
1223 /* get the current settings as a dictionary */
1224 dictObjPtr = Jim_GetTtySettings(interp, af->fd);
1225 if (dictObjPtr == NULL) {
1226 JimAioSetError(interp, NULL);
1227 return JIM_ERR;
1229 Jim_SetResult(interp, dictObjPtr);
1230 return JIM_OK;
1233 if (argc > 1) {
1234 /* Convert name value arguments to a dictionary */
1235 dictObjPtr = Jim_NewListObj(interp, argv, argc);
1237 else {
1238 /* The settings are already given as a list */
1239 dictObjPtr = argv[0];
1241 Jim_IncrRefCount(dictObjPtr);
1243 if (Jim_ListLength(interp, dictObjPtr) % 2) {
1244 /* Must be a valid dictionary */
1245 Jim_DecrRefCount(interp, dictObjPtr);
1246 return -1;
1249 ret = Jim_SetTtySettings(interp, af->fd, dictObjPtr);
1250 if (ret < 0) {
1251 JimAioSetError(interp, NULL);
1252 ret = JIM_ERR;
1254 Jim_DecrRefCount(interp, dictObjPtr);
1256 return ret;
1258 #endif /* JIM_BOOTSTRAP */
1260 static const jim_subcmd_type aio_command_table[] = {
1261 { "read",
1262 "?-nonewline? ?len?",
1263 aio_cmd_read,
1266 /* Description: Read and return bytes from the stream. To eof if no len. */
1268 { "copyto",
1269 "handle ?size?",
1270 aio_cmd_copy,
1273 /* Description: Copy up to 'size' bytes to the given filehandle, or to eof if no size. */
1275 { "gets",
1276 "?var?",
1277 aio_cmd_gets,
1280 /* Description: Read one line and return it or store it in the var */
1282 { "puts",
1283 "?-nonewline? str",
1284 aio_cmd_puts,
1287 /* Description: Write the string, with newline unless -nonewline */
1289 { "isatty",
1290 NULL,
1291 aio_cmd_isatty,
1294 /* Description: Is the file descriptor a tty? */
1296 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
1297 { "recvfrom",
1298 "len ?addrvar?",
1299 aio_cmd_recvfrom,
1302 /* Description: Receive up to 'len' bytes on the socket. Sets 'addrvar' with receive address, if set */
1304 { "sendto",
1305 "str address",
1306 aio_cmd_sendto,
1309 /* Description: Send 'str' to the given address (dgram only) */
1311 { "accept",
1312 "?addrvar?",
1313 aio_cmd_accept,
1316 /* Description: Server socket only: Accept a connection and return stream */
1318 { "listen",
1319 "backlog",
1320 aio_cmd_listen,
1323 /* Description: Set the listen backlog for server socket */
1325 #endif /* JIM_BOOTSTRAP */
1326 { "flush",
1327 NULL,
1328 aio_cmd_flush,
1331 /* Description: Flush the stream */
1333 { "eof",
1334 NULL,
1335 aio_cmd_eof,
1338 /* Description: Returns 1 if stream is at eof */
1340 { "close",
1341 "?r(ead)|w(rite)?",
1342 aio_cmd_close,
1345 JIM_MODFLAG_FULLARGV,
1346 /* Description: Closes the stream. */
1348 { "seek",
1349 "offset ?start|current|end",
1350 aio_cmd_seek,
1353 /* Description: Seeks in the stream (default 'current') */
1355 { "tell",
1356 NULL,
1357 aio_cmd_tell,
1360 /* Description: Returns the current seek position */
1362 { "filename",
1363 NULL,
1364 aio_cmd_filename,
1367 /* Description: Returns the original filename */
1369 #ifdef O_NDELAY
1370 { "ndelay",
1371 "?0|1?",
1372 aio_cmd_ndelay,
1375 /* Description: Set O_NDELAY (if arg). Returns current/new setting. */
1377 #endif
1378 #ifdef HAVE_FSYNC
1379 { "sync",
1380 NULL,
1381 aio_cmd_sync,
1384 /* Description: Flush and fsync() the stream */
1386 #endif
1387 { "buffering",
1388 "none|line|full",
1389 aio_cmd_buffering,
1392 /* Description: Sets buffering */
1394 #ifdef jim_ext_eventloop
1395 { "readable",
1396 "?readable-script?",
1397 aio_cmd_readable,
1400 /* Description: Returns script, or invoke readable-script when readable, {} to remove */
1402 { "writable",
1403 "?writable-script?",
1404 aio_cmd_writable,
1407 /* Description: Returns script, or invoke writable-script when writable, {} to remove */
1409 { "onexception",
1410 "?exception-script?",
1411 aio_cmd_onexception,
1414 /* Description: Returns script, or invoke exception-script when oob data, {} to remove */
1416 #endif
1417 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1418 { "ssl",
1419 "?-server cert priv?",
1420 aio_cmd_ssl,
1423 JIM_MODFLAG_FULLARGV
1424 /* Description: Wraps a stream socket with SSL/TLS and returns a new channel */
1426 { "verify",
1427 NULL,
1428 aio_cmd_verify,
1431 /* Description: Verifies the certificate of a SSL/TLS channel */
1433 #endif /* JIM_BOOTSTRAP */
1434 #if defined(HAVE_STRUCT_FLOCK) && !defined(JIM_BOOTSTRAP)
1435 { "lock",
1436 NULL,
1437 aio_cmd_lock,
1440 /* Description: Attempt to get a lock. */
1442 { "unlock",
1443 NULL,
1444 aio_cmd_unlock,
1447 /* Description: Relase a lock. */
1449 #endif /* JIM_BOOTSTRAP */
1450 #if defined(HAVE_TERMIOS_H) && !defined(JIM_BOOTSTRAP)
1451 { "tty",
1452 "?baud rate? ?data bits? ?stop bits? ?parity even|odd|none? ?handshake xonxoff|rtscts|none? ?input raw|cooked? ?output raw|cooked? ?vmin n? ?vtime n?",
1453 aio_cmd_tty,
1456 /* Description: Get or set tty settings - valid only on a tty */
1458 #endif /* JIM_BOOTSTRAP */
1459 { NULL }
1462 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1464 return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, aio_command_table, argc, argv), argc, argv);
1467 static int JimAioOpenCommand(Jim_Interp *interp, int argc,
1468 Jim_Obj *const *argv)
1470 const char *mode;
1472 if (argc != 2 && argc != 3) {
1473 Jim_WrongNumArgs(interp, 1, argv, "filename ?mode?");
1474 return JIM_ERR;
1477 mode = (argc == 3) ? Jim_String(argv[2]) : "r";
1479 #ifdef jim_ext_tclcompat
1481 const char *filename = Jim_String(argv[1]);
1483 /* If the filename starts with '|', use popen instead */
1484 if (*filename == '|') {
1485 Jim_Obj *evalObj[3];
1487 evalObj[0] = Jim_NewStringObj(interp, "::popen", -1);
1488 evalObj[1] = Jim_NewStringObj(interp, filename + 1, -1);
1489 evalObj[2] = Jim_NewStringObj(interp, mode, -1);
1491 return Jim_EvalObjVector(interp, 3, evalObj);
1494 #endif
1495 return JimMakeChannel(interp, NULL, -1, argv[1], "aio.handle%ld", 0, mode) ? JIM_OK : JIM_ERR;
1498 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1499 static void JimAioSslContextDelProc(struct Jim_Interp *interp, void *privData)
1501 SSL_CTX_free((SSL_CTX *)privData);
1502 ERR_free_strings();
1505 static SSL_CTX *JimAioSslCtx(Jim_Interp *interp)
1507 SSL_CTX *ssl_ctx = (SSL_CTX *)Jim_GetAssocData(interp, "ssl_ctx");
1508 if (ssl_ctx == NULL) {
1509 SSL_load_error_strings();
1510 SSL_library_init();
1511 ssl_ctx = SSL_CTX_new(TLSv1_2_method());
1512 if (ssl_ctx && SSL_CTX_set_default_verify_paths(ssl_ctx)) {
1513 SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
1514 Jim_SetAssocData(interp, "ssl_ctx", JimAioSslContextDelProc, ssl_ctx);
1515 } else {
1516 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1519 return ssl_ctx;
1521 #endif /* JIM_BOOTSTRAP */
1524 * Creates a channel for fh/fd/filename.
1526 * If fh is not NULL, uses that as the channel (and sets AIO_KEEPOPEN).
1527 * Otherwise, if fd is >= 0, uses that as the channel.
1528 * Otherwise opens 'filename' with mode 'mode'.
1530 * hdlfmt is a sprintf format for the filehandle. Anything with %ld at the end will do.
1531 * mode is used for open or fdopen.
1533 * Creates the command and sets the name as the current result.
1534 * Returns the AioFile pointer on sucess or NULL on failure.
1536 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
1537 const char *hdlfmt, int family, const char *mode)
1539 AioFile *af;
1540 char buf[AIO_CMD_LEN];
1541 int openFlags = 0;
1543 snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
1545 if (fh) {
1546 openFlags = AIO_KEEPOPEN;
1549 snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
1550 if (!filename) {
1551 filename = Jim_NewStringObj(interp, buf, -1);
1554 Jim_IncrRefCount(filename);
1556 if (fh == NULL) {
1557 #if !defined(JIM_ANSIC)
1558 if (fd >= 0) {
1559 fh = fdopen(fd, mode);
1561 else
1562 #endif
1563 fh = fopen(Jim_String(filename), mode);
1565 if (fh == NULL) {
1566 JimAioSetError(interp, filename);
1567 #if !defined(JIM_ANSIC)
1568 if (fd >= 0) {
1569 close(fd);
1571 #endif
1572 Jim_DecrRefCount(interp, filename);
1573 return NULL;
1577 /* Create the file command */
1578 af = Jim_Alloc(sizeof(*af));
1579 memset(af, 0, sizeof(*af));
1580 af->fp = fh;
1581 af->fd = fileno(fh);
1582 af->filename = filename;
1583 #ifdef FD_CLOEXEC
1584 if ((openFlags & AIO_KEEPOPEN) == 0) {
1585 (void)fcntl(af->fd, F_SETFD, FD_CLOEXEC);
1587 #endif
1588 af->openFlags = openFlags;
1589 af->addr_family = family;
1590 af->fops = &stdio_fops;
1591 af->ssl = NULL;
1593 Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc);
1595 /* Note that the command must use the global namespace, even if
1596 * the current namespace is something different
1598 Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, Jim_NewStringObj(interp, buf, -1)));
1600 return af;
1603 #if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && defined(HAVE_SYS_UN_H))
1605 * Create a pair of channels. e.g. from pipe() or socketpair()
1607 static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename,
1608 const char *hdlfmt, int family, const char *mode[2])
1610 if (JimMakeChannel(interp, NULL, p[0], filename, hdlfmt, family, mode[0])) {
1611 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
1612 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
1614 if (JimMakeChannel(interp, NULL, p[1], filename, hdlfmt, family, mode[1])) {
1615 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
1616 Jim_SetResult(interp, objPtr);
1617 return JIM_OK;
1621 /* Can only be here if fdopen() failed */
1622 close(p[0]);
1623 close(p[1]);
1624 JimAioSetError(interp, NULL);
1625 return JIM_ERR;
1627 #endif
1629 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
1631 static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1633 const char *hdlfmt = "aio.unknown%ld";
1634 const char *socktypes[] = {
1635 "unix",
1636 "unix.server",
1637 "dgram",
1638 "dgram.server",
1639 "stream",
1640 "stream.server",
1641 "pipe",
1642 "pair",
1643 NULL
1645 enum
1647 SOCK_UNIX,
1648 SOCK_UNIX_SERVER,
1649 SOCK_DGRAM_CLIENT,
1650 SOCK_DGRAM_SERVER,
1651 SOCK_STREAM_CLIENT,
1652 SOCK_STREAM_SERVER,
1653 SOCK_STREAM_PIPE,
1654 SOCK_STREAM_SOCKETPAIR,
1656 int socktype;
1657 int sock;
1658 const char *hostportarg = NULL;
1659 int res;
1660 int on = 1;
1661 const char *mode = "r+";
1662 int family = PF_INET;
1663 Jim_Obj *argv0 = argv[0];
1664 int ipv6 = 0;
1666 if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-ipv6")) {
1667 if (!IPV6) {
1668 Jim_SetResultString(interp, "ipv6 not supported", -1);
1669 return JIM_ERR;
1671 ipv6 = 1;
1672 family = PF_INET6;
1674 argc -= ipv6;
1675 argv += ipv6;
1677 if (argc < 2) {
1678 wrongargs:
1679 Jim_WrongNumArgs(interp, 1, &argv0, "?-ipv6? type ?address?");
1680 return JIM_ERR;
1683 if (Jim_GetEnum(interp, argv[1], socktypes, &socktype, "socket type", JIM_ERRMSG) != JIM_OK)
1684 return Jim_CheckShowCommands(interp, argv[1], socktypes);
1686 Jim_SetEmptyResult(interp);
1688 hdlfmt = "aio.sock%ld";
1690 if (argc > 2) {
1691 hostportarg = Jim_String(argv[2]);
1694 switch (socktype) {
1695 case SOCK_DGRAM_CLIENT:
1696 if (argc == 2) {
1697 /* No address, so an unconnected dgram socket */
1698 sock = socket(family, SOCK_DGRAM, 0);
1699 if (sock < 0) {
1700 JimAioSetError(interp, NULL);
1701 return JIM_ERR;
1703 break;
1705 /* fall through */
1706 case SOCK_STREAM_CLIENT:
1708 union sockaddr_any sa;
1709 int salen;
1711 if (argc != 3) {
1712 goto wrongargs;
1715 if (ipv6) {
1716 if (JimParseIPv6Address(interp, hostportarg, &sa, &salen) != JIM_OK) {
1717 return JIM_ERR;
1720 else if (JimParseIpAddress(interp, hostportarg, &sa, &salen) != JIM_OK) {
1721 return JIM_ERR;
1723 sock = socket(family, (socktype == SOCK_DGRAM_CLIENT) ? SOCK_DGRAM : SOCK_STREAM, 0);
1724 if (sock < 0) {
1725 JimAioSetError(interp, NULL);
1726 return JIM_ERR;
1728 res = connect(sock, &sa.sa, salen);
1729 if (res) {
1730 JimAioSetError(interp, argv[2]);
1731 close(sock);
1732 return JIM_ERR;
1735 break;
1737 case SOCK_STREAM_SERVER:
1738 case SOCK_DGRAM_SERVER:
1740 union sockaddr_any sa;
1741 int salen;
1743 if (argc != 3) {
1744 goto wrongargs;
1747 if (ipv6) {
1748 if (JimParseIPv6Address(interp, hostportarg, &sa, &salen) != JIM_OK) {
1749 return JIM_ERR;
1752 else if (JimParseIpAddress(interp, hostportarg, &sa, &salen) != JIM_OK) {
1753 return JIM_ERR;
1755 sock = socket(family, (socktype == SOCK_DGRAM_SERVER) ? SOCK_DGRAM : SOCK_STREAM, 0);
1756 if (sock < 0) {
1757 JimAioSetError(interp, NULL);
1758 return JIM_ERR;
1761 /* Enable address reuse */
1762 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
1764 res = bind(sock, &sa.sa, salen);
1765 if (res) {
1766 JimAioSetError(interp, argv[2]);
1767 close(sock);
1768 return JIM_ERR;
1770 if (socktype == SOCK_STREAM_SERVER) {
1771 res = listen(sock, 5);
1772 if (res) {
1773 JimAioSetError(interp, NULL);
1774 close(sock);
1775 return JIM_ERR;
1778 hdlfmt = "aio.socksrv%ld";
1780 break;
1782 #ifdef HAVE_SYS_UN_H
1783 case SOCK_UNIX:
1785 struct sockaddr_un sa;
1786 socklen_t len;
1788 if (argc != 3 || ipv6) {
1789 goto wrongargs;
1792 if (JimParseDomainAddress(interp, hostportarg, &sa) != JIM_OK) {
1793 JimAioSetError(interp, argv[2]);
1794 return JIM_ERR;
1796 family = PF_UNIX;
1797 sock = socket(PF_UNIX, SOCK_STREAM, 0);
1798 if (sock < 0) {
1799 JimAioSetError(interp, NULL);
1800 return JIM_ERR;
1802 len = strlen(sa.sun_path) + 1 + sizeof(sa.sun_family);
1803 res = connect(sock, (struct sockaddr *)&sa, len);
1804 if (res) {
1805 JimAioSetError(interp, argv[2]);
1806 close(sock);
1807 return JIM_ERR;
1809 hdlfmt = "aio.sockunix%ld";
1810 break;
1813 case SOCK_UNIX_SERVER:
1815 struct sockaddr_un sa;
1816 socklen_t len;
1818 if (argc != 3 || ipv6) {
1819 goto wrongargs;
1822 if (JimParseDomainAddress(interp, hostportarg, &sa) != JIM_OK) {
1823 JimAioSetError(interp, argv[2]);
1824 return JIM_ERR;
1826 family = PF_UNIX;
1827 sock = socket(PF_UNIX, SOCK_STREAM, 0);
1828 if (sock < 0) {
1829 JimAioSetError(interp, NULL);
1830 return JIM_ERR;
1832 len = strlen(sa.sun_path) + 1 + sizeof(sa.sun_family);
1833 res = bind(sock, (struct sockaddr *)&sa, len);
1834 if (res) {
1835 JimAioSetError(interp, argv[2]);
1836 close(sock);
1837 return JIM_ERR;
1839 res = listen(sock, 5);
1840 if (res) {
1841 JimAioSetError(interp, NULL);
1842 close(sock);
1843 return JIM_ERR;
1845 hdlfmt = "aio.sockunixsrv%ld";
1846 break;
1848 #endif
1850 #if defined(HAVE_SOCKETPAIR) && defined(HAVE_SYS_UN_H)
1851 case SOCK_STREAM_SOCKETPAIR:
1853 int p[2];
1854 static const char *mode[2] = { "r+", "r+" };
1856 if (argc != 2 || ipv6) {
1857 goto wrongargs;
1860 if (socketpair(PF_UNIX, SOCK_STREAM, 0, p) < 0) {
1861 JimAioSetError(interp, NULL);
1862 return JIM_ERR;
1864 return JimMakeChannelPair(interp, p, argv[1], "aio.sockpair%ld", PF_UNIX, mode);
1866 break;
1867 #endif
1869 #if defined(HAVE_PIPE)
1870 case SOCK_STREAM_PIPE:
1872 int p[2];
1873 static const char *mode[2] = { "r", "w" };
1875 if (argc != 2 || ipv6) {
1876 goto wrongargs;
1879 if (pipe(p) < 0) {
1880 JimAioSetError(interp, NULL);
1881 return JIM_ERR;
1884 return JimMakeChannelPair(interp, p, argv[1], "aio.pipe%ld", 0, mode);
1886 break;
1887 #endif
1889 default:
1890 Jim_SetResultString(interp, "Unsupported socket type", -1);
1891 return JIM_ERR;
1894 return JimMakeChannel(interp, NULL, sock, argv[1], hdlfmt, family, mode) ? JIM_OK : JIM_ERR;
1896 #endif /* JIM_BOOTSTRAP */
1899 * Returns the file descriptor of a writable, newly created temp file
1900 * or -1 on error.
1902 * On success, leaves the filename in the interpreter result, otherwise
1903 * leaves an error message.
1905 int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template)
1907 #ifdef HAVE_MKSTEMP
1908 int fd;
1909 mode_t mask;
1910 Jim_Obj *filenameObj;
1912 if (filename_template == NULL) {
1913 const char *tmpdir = getenv("TMPDIR");
1914 if (tmpdir == NULL || *tmpdir == '\0' || access(tmpdir, W_OK) != 0) {
1915 tmpdir = "/tmp/";
1917 filenameObj = Jim_NewStringObj(interp, tmpdir, -1);
1918 if (tmpdir[0] && tmpdir[strlen(tmpdir) - 1] != '/') {
1919 Jim_AppendString(interp, filenameObj, "/", 1);
1921 Jim_AppendString(interp, filenameObj, "tcl.tmp.XXXXXX", -1);
1923 else {
1924 filenameObj = Jim_NewStringObj(interp, filename_template, -1);
1927 /* Update the template name directly with the filename */
1928 mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
1929 fd = mkstemp(filenameObj->bytes);
1930 umask(mask);
1931 if (fd < 0) {
1932 JimAioSetError(interp, filenameObj);
1933 Jim_FreeNewObj(interp, filenameObj);
1934 return -1;
1937 Jim_SetResult(interp, filenameObj);
1938 return fd;
1939 #else
1940 Jim_SetResultString(interp, "platform has no tempfile support", -1);
1941 return -1;
1942 #endif
1945 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1946 static int JimAioLoadSSLCertsCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1948 SSL_CTX *ssl_ctx;
1950 if (argc != 2) {
1951 Jim_WrongNumArgs(interp, 1, argv, "dir");
1952 return JIM_ERR;
1955 ssl_ctx = JimAioSslCtx(interp);
1956 if (!ssl_ctx) {
1957 return JIM_ERR;
1959 if (SSL_CTX_load_verify_locations(ssl_ctx, NULL, Jim_String(argv[1])) == 1) {
1960 return JIM_OK;
1962 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1963 return JIM_ERR;
1965 #endif /* JIM_BOOTSTRAP */
1967 int Jim_aioInit(Jim_Interp *interp)
1969 if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG))
1970 return JIM_ERR;
1972 #if defined(JIM_SSL)
1973 Jim_CreateCommand(interp, "load_ssl_certs", JimAioLoadSSLCertsCommand, NULL, NULL);
1974 #endif
1976 Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL);
1977 #ifndef JIM_ANSIC
1978 Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL);
1979 #endif
1981 /* Create filehandles for stdin, stdout and stderr */
1982 JimMakeChannel(interp, stdin, -1, NULL, "stdin", 0, "r");
1983 JimMakeChannel(interp, stdout, -1, NULL, "stdout", 0, "w");
1984 JimMakeChannel(interp, stderr, -1, NULL, "stderr", 0, "w");
1986 return JIM_OK;