tcltest: do a better job of cleanup up after tests
[jimtcl.git] / jim-aio.c
blobe8af2f38f461fff93ad26ea488dad01eeba4712e
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 #define _GNU_SOURCE
43 #include <stdio.h>
44 #include <string.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #ifdef HAVE_UNISTD_H
48 #include <unistd.h>
49 #include <sys/stat.h>
50 #endif
52 #include "jim.h"
54 #if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H)
55 #include <sys/socket.h>
56 #include <netinet/in.h>
57 #include <arpa/inet.h>
58 #include <netdb.h>
59 #ifdef HAVE_SYS_UN_H
60 #include <sys/un.h>
61 #endif
62 #else
63 #define JIM_ANSIC
64 #endif
66 #if defined(JIM_SSL)
67 #include <openssl/ssl.h>
68 #include <openssl/err.h>
69 #endif
71 #ifdef HAVE_TERMIOS_H
72 #include <jim-tty.h>
73 #endif
75 #include "jim-eventloop.h"
76 #include "jim-subcmd.h"
78 #define AIO_CMD_LEN 32 /* e.g. aio.handleXXXXXX */
79 #define AIO_BUF_LEN 256 /* Can keep this small and rely on stdio buffering */
81 #ifndef HAVE_FTELLO
82 #define ftello ftell
83 #endif
84 #ifndef HAVE_FSEEKO
85 #define fseeko fseek
86 #endif
88 #define AIO_KEEPOPEN 1
90 #if defined(JIM_IPV6)
91 #define IPV6 1
92 #else
93 #define IPV6 0
94 #ifndef PF_INET6
95 #define PF_INET6 0
96 #endif
97 #endif
99 #define JimCheckStreamError(interp, af) af->fops->error(af)
101 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
102 union sockaddr_any {
103 struct sockaddr sa;
104 struct sockaddr_in sin;
105 #if IPV6
106 struct sockaddr_in6 sin6;
107 #endif
110 #ifndef HAVE_INET_NTOP
111 const char *inet_ntop(int af, const void *src, char *dst, int size)
113 if (af != PF_INET) {
114 return NULL;
116 snprintf(dst, size, "%s", inet_ntoa(((struct sockaddr_in *)src)->sin_addr));
117 return dst;
119 #endif
120 #endif /* JIM_BOOTSTRAP */
122 struct AioFile;
124 typedef struct {
125 int (*writer)(struct AioFile *af, const char *buf, int len);
126 int (*reader)(struct AioFile *af, char *buf, int len);
127 const char *(*getline)(struct AioFile *af, char *buf, int len);
128 int (*error)(const struct AioFile *af);
129 const char *(*strerror)(struct AioFile *af);
130 int (*verify)(struct AioFile *af);
131 } JimAioFopsType;
133 typedef struct AioFile
135 FILE *fp;
136 Jim_Obj *filename;
137 int type;
138 int openFlags; /* AIO_KEEPOPEN? keep FILE* */
139 int fd;
140 Jim_Obj *rEvent;
141 Jim_Obj *wEvent;
142 Jim_Obj *eEvent;
143 int addr_family;
144 void *ssl;
145 const JimAioFopsType *fops;
146 } AioFile;
148 static int stdio_writer(struct AioFile *af, const char *buf, int len)
150 return fwrite(buf, 1, len, af->fp);
153 static int stdio_reader(struct AioFile *af, char *buf, int len)
155 return fread(buf, 1, len, af->fp);
158 static const char *stdio_getline(struct AioFile *af, char *buf, int len)
160 return fgets(buf, len, af->fp);
163 static int stdio_error(const AioFile *af)
165 if (!ferror(af->fp)) {
166 return JIM_OK;
168 clearerr(af->fp);
169 /* EAGAIN and similar are not error conditions. Just treat them like eof */
170 if (feof(af->fp) || errno == EAGAIN || errno == EINTR) {
171 return JIM_OK;
173 #ifdef ECONNRESET
174 if (errno == ECONNRESET) {
175 return JIM_OK;
177 #endif
178 #ifdef ECONNABORTED
179 if (errno != ECONNABORTED) {
180 return JIM_OK;
182 #endif
183 return JIM_ERR;
186 static const char *stdio_strerror(struct AioFile *af)
188 return strerror(errno);
191 static const JimAioFopsType stdio_fops = {
192 stdio_writer,
193 stdio_reader,
194 stdio_getline,
195 stdio_error,
196 stdio_strerror,
197 NULL
200 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
202 static SSL_CTX *JimAioSslCtx(Jim_Interp *interp);
204 static int ssl_writer(struct AioFile *af, const char *buf, int len)
206 return SSL_write(af->ssl, buf, len);
209 static int ssl_reader(struct AioFile *af, char *buf, int len)
211 return SSL_read(af->ssl, buf, len);
214 static const char *ssl_getline(struct AioFile *af, char *buf, int len)
216 int i;
217 for (i = 0; i < len + 1; i++) {
218 if (SSL_read(af->ssl, &buf[i], 1) != 1) {
219 if (i == 0) {
220 return NULL;
222 break;
224 if (buf[i] == '\n') {
225 break;
228 buf[i] = '\0';
229 return buf;
232 static int ssl_error(const struct AioFile *af)
234 if (ERR_peek_error() == 0) {
235 return JIM_OK;
238 return JIM_ERR;
241 static const char *ssl_strerror(struct AioFile *af)
243 int err = ERR_get_error();
245 if (err) {
246 return ERR_error_string(err, NULL);
249 /* should not happen */
250 return "unknown SSL error";
253 static int ssl_verify(struct AioFile *af)
255 X509 *cert;
257 cert = SSL_get_peer_certificate(af->ssl);
258 if (!cert) {
259 return JIM_ERR;
261 X509_free(cert);
263 if (SSL_get_verify_result(af->ssl) == X509_V_OK) {
264 return JIM_OK;
267 return JIM_ERR;
270 static const JimAioFopsType ssl_fops = {
271 ssl_writer,
272 ssl_reader,
273 ssl_getline,
274 ssl_error,
275 ssl_strerror,
276 ssl_verify
278 #endif /* JIM_BOOTSTRAP */
280 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
281 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
282 const char *hdlfmt, int family, const char *mode);
284 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
285 static int JimParseIPv6Address(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, int *salen)
287 #if IPV6
289 * An IPv6 addr/port looks like:
290 * [::1]
291 * [::1]:2000
292 * [fe80::223:6cff:fe95:bdc0%en1]:2000
293 * [::]:2000
294 * 2000
296 * Note that the "any" address is ::, which is the same as when no address is specified.
298 char *sthost = NULL;
299 const char *stport;
300 int ret = JIM_OK;
301 struct addrinfo req;
302 struct addrinfo *ai;
304 stport = strrchr(hostport, ':');
305 if (!stport) {
306 /* No : so, the whole thing is the port */
307 stport = hostport;
308 hostport = "::";
309 sthost = Jim_StrDup(hostport);
311 else {
312 stport++;
315 if (*hostport == '[') {
316 /* This is a numeric ipv6 address */
317 char *pt = strchr(++hostport, ']');
318 if (pt) {
319 sthost = Jim_StrDupLen(hostport, pt - hostport);
323 if (!sthost) {
324 sthost = Jim_StrDupLen(hostport, stport - hostport - 1);
327 memset(&req, '\0', sizeof(req));
328 req.ai_family = PF_INET6;
330 if (getaddrinfo(sthost, NULL, &req, &ai)) {
331 Jim_SetResultFormatted(interp, "Not a valid address: %s", hostport);
332 ret = JIM_ERR;
334 else {
335 memcpy(&sa->sin, ai->ai_addr, ai->ai_addrlen);
336 *salen = ai->ai_addrlen;
338 sa->sin.sin_port = htons(atoi(stport));
340 freeaddrinfo(ai);
342 Jim_Free(sthost);
344 return ret;
345 #else
346 Jim_SetResultString(interp, "ipv6 not supported", -1);
347 return JIM_ERR;
348 #endif
351 static int JimParseIpAddress(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, int *salen)
353 /* An IPv4 addr/port looks like:
354 * 192.168.1.5
355 * 192.168.1.5:2000
356 * 2000
358 * If the address is missing, INADDR_ANY is used.
359 * If the port is missing, 0 is used (only useful for server sockets).
361 char *sthost = NULL;
362 const char *stport;
363 int ret = JIM_OK;
365 stport = strrchr(hostport, ':');
366 if (!stport) {
367 /* No : so, the whole thing is the port */
368 stport = hostport;
369 sthost = Jim_StrDup("0.0.0.0");
371 else {
372 sthost = Jim_StrDupLen(hostport, stport - hostport);
373 stport++;
377 #ifdef HAVE_GETADDRINFO
378 struct addrinfo req;
379 struct addrinfo *ai;
380 memset(&req, '\0', sizeof(req));
381 req.ai_family = PF_INET;
383 if (getaddrinfo(sthost, NULL, &req, &ai)) {
384 ret = JIM_ERR;
386 else {
387 memcpy(&sa->sin, ai->ai_addr, ai->ai_addrlen);
388 *salen = ai->ai_addrlen;
389 freeaddrinfo(ai);
391 #else
392 struct hostent *he;
394 ret = JIM_ERR;
396 if ((he = gethostbyname(sthost)) != NULL) {
397 if (he->h_length == sizeof(sa->sin.sin_addr)) {
398 *salen = sizeof(sa->sin);
399 sa->sin.sin_family= he->h_addrtype;
400 memcpy(&sa->sin.sin_addr, he->h_addr, he->h_length); /* set address */
401 ret = JIM_OK;
404 #endif
406 sa->sin.sin_port = htons(atoi(stport));
408 Jim_Free(sthost);
410 if (ret != JIM_OK) {
411 Jim_SetResultFormatted(interp, "Not a valid address: %s", hostport);
414 return ret;
417 #ifdef HAVE_SYS_UN_H
418 static int JimParseDomainAddress(Jim_Interp *interp, const char *path, struct sockaddr_un *sa)
420 sa->sun_family = PF_UNIX;
421 snprintf(sa->sun_path, sizeof(sa->sun_path), "%s", path);
423 return JIM_OK;
425 #endif
428 * Format that address in 'sa' as a string and store in variable 'varObjPtr'
430 static int JimFormatIpAddress(Jim_Interp *interp, Jim_Obj *varObjPtr, const union sockaddr_any *sa)
432 /* INET6_ADDRSTRLEN is 46. Add some for [] and port */
433 char addrbuf[60];
435 #if IPV6
436 if (sa->sa.sa_family == PF_INET6) {
437 addrbuf[0] = '[';
438 /* Allow 9 for []:65535\0 */
439 inet_ntop(sa->sa.sa_family, &sa->sin6.sin6_addr, addrbuf + 1, sizeof(addrbuf) - 9);
440 snprintf(addrbuf + strlen(addrbuf), 8, "]:%d", ntohs(sa->sin.sin_port));
442 else
443 #endif
444 if (sa->sa.sa_family == PF_INET) {
445 /* Allow 7 for :65535\0 */
446 inet_ntop(sa->sa.sa_family, &sa->sin.sin_addr, addrbuf, sizeof(addrbuf) - 7);
447 snprintf(addrbuf + strlen(addrbuf), 7, ":%d", ntohs(sa->sin.sin_port));
449 else {
450 /* recvfrom still works on unix domain sockets, etc */
451 addrbuf[0] = 0;
454 return Jim_SetVariable(interp, varObjPtr, Jim_NewStringObj(interp, addrbuf, -1));
457 #endif /* JIM_BOOTSTRAP */
459 static const char *JimAioErrorString(AioFile *af)
461 if (af && af->fops)
462 return af->fops->strerror(af);
464 return strerror(errno);
467 static void JimAioSetError(Jim_Interp *interp, Jim_Obj *name)
469 AioFile *af = Jim_CmdPrivData(interp);
471 if (name) {
472 Jim_SetResultFormatted(interp, "%#s: %s", name, JimAioErrorString(af));
474 else {
475 Jim_SetResultString(interp, JimAioErrorString(af), -1);
479 static void JimAioDelProc(Jim_Interp *interp, void *privData)
481 AioFile *af = privData;
483 JIM_NOTUSED(interp);
485 Jim_DecrRefCount(interp, af->filename);
487 #ifdef jim_ext_eventloop
488 /* remove all existing EventHandlers */
489 Jim_DeleteFileHandler(interp, af->fd, JIM_EVENT_READABLE | JIM_EVENT_WRITABLE | JIM_EVENT_EXCEPTION);
490 #endif
492 #if defined(JIM_SSL)
493 if (af->ssl != NULL) {
494 SSL_free(af->ssl);
496 #endif
497 if (!(af->openFlags & AIO_KEEPOPEN)) {
498 fclose(af->fp);
501 Jim_Free(af);
504 static int aio_cmd_read(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
506 AioFile *af = Jim_CmdPrivData(interp);
507 char buf[AIO_BUF_LEN];
508 Jim_Obj *objPtr;
509 int nonewline = 0;
510 jim_wide neededLen = -1; /* -1 is "read as much as possible" */
512 if (argc && Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
513 nonewline = 1;
514 argv++;
515 argc--;
517 if (argc == 1) {
518 if (Jim_GetWide(interp, argv[0], &neededLen) != JIM_OK)
519 return JIM_ERR;
520 if (neededLen < 0) {
521 Jim_SetResultString(interp, "invalid parameter: negative len", -1);
522 return JIM_ERR;
525 else if (argc) {
526 return -1;
528 objPtr = Jim_NewStringObj(interp, NULL, 0);
529 while (neededLen != 0) {
530 int retval;
531 int readlen;
533 if (neededLen == -1) {
534 readlen = AIO_BUF_LEN;
536 else {
537 readlen = (neededLen > AIO_BUF_LEN ? AIO_BUF_LEN : neededLen);
539 retval = af->fops->reader(af, buf, readlen);
540 if (retval > 0) {
541 Jim_AppendString(interp, objPtr, buf, retval);
542 if (neededLen != -1) {
543 neededLen -= retval;
546 if (retval != readlen)
547 break;
549 /* Check for error conditions */
550 if (JimCheckStreamError(interp, af)) {
551 Jim_FreeNewObj(interp, objPtr);
552 return JIM_ERR;
554 if (nonewline) {
555 int len;
556 const char *s = Jim_GetString(objPtr, &len);
558 if (len > 0 && s[len - 1] == '\n') {
559 objPtr->length--;
560 objPtr->bytes[objPtr->length] = '\0';
563 Jim_SetResult(interp, objPtr);
564 return JIM_OK;
567 AioFile *Jim_AioFile(Jim_Interp *interp, Jim_Obj *command)
569 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, command, JIM_ERRMSG);
571 /* XXX: There ought to be a supported API for this */
572 if (cmdPtr && !cmdPtr->isproc && cmdPtr->u.native.cmdProc == JimAioSubCmdProc) {
573 return (AioFile *) cmdPtr->u.native.privData;
575 Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", command);
576 return NULL;
579 FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command)
581 AioFile *af;
583 af = Jim_AioFile(interp, command);
584 if (af == NULL) {
585 return NULL;
588 return af->fp;
591 static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
593 AioFile *af = Jim_CmdPrivData(interp);
594 jim_wide count = 0;
595 jim_wide maxlen = JIM_WIDE_MAX;
596 AioFile *outf = Jim_AioFile(interp, argv[0]);
598 if (outf == NULL) {
599 return JIM_ERR;
602 if (argc == 2) {
603 if (Jim_GetWide(interp, argv[1], &maxlen) != JIM_OK) {
604 return JIM_ERR;
608 while (count < maxlen) {
609 char ch;
611 if (af->fops->reader(af, &ch, 1) != 1) {
612 break;
614 if (outf->fops->writer(outf, &ch, 1) != 1) {
615 break;
617 count++;
620 if (JimCheckStreamError(interp, af) || JimCheckStreamError(interp, outf)) {
621 return JIM_ERR;
624 Jim_SetResultInt(interp, count);
626 return JIM_OK;
629 static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
631 AioFile *af = Jim_CmdPrivData(interp);
632 char buf[AIO_BUF_LEN];
633 Jim_Obj *objPtr;
634 int len;
636 errno = 0;
638 objPtr = Jim_NewStringObj(interp, NULL, 0);
639 while (1) {
640 buf[AIO_BUF_LEN - 1] = '_';
642 if (af->fops->getline(af, buf, AIO_BUF_LEN) == NULL)
643 break;
645 if (buf[AIO_BUF_LEN - 1] == '\0' && buf[AIO_BUF_LEN - 2] != '\n') {
646 Jim_AppendString(interp, objPtr, buf, AIO_BUF_LEN - 1);
648 else {
649 len = strlen(buf);
651 if (len && (buf[len - 1] == '\n')) {
652 /* strip "\n" */
653 len--;
656 Jim_AppendString(interp, objPtr, buf, len);
657 break;
661 if (JimCheckStreamError(interp, af)) {
662 /* I/O error */
663 Jim_FreeNewObj(interp, objPtr);
664 return JIM_ERR;
667 if (argc) {
668 if (Jim_SetVariable(interp, argv[0], objPtr) != JIM_OK) {
669 Jim_FreeNewObj(interp, objPtr);
670 return JIM_ERR;
673 len = Jim_Length(objPtr);
675 if (len == 0 && feof(af->fp)) {
676 /* On EOF returns -1 if varName was specified */
677 len = -1;
679 Jim_SetResultInt(interp, len);
681 else {
682 Jim_SetResult(interp, objPtr);
684 return JIM_OK;
687 static int aio_cmd_puts(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
689 AioFile *af = Jim_CmdPrivData(interp);
690 int wlen;
691 const char *wdata;
692 Jim_Obj *strObj;
694 if (argc == 2) {
695 if (!Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
696 return -1;
698 strObj = argv[1];
700 else {
701 strObj = argv[0];
704 wdata = Jim_GetString(strObj, &wlen);
705 if (af->fops->writer(af, wdata, wlen) == wlen) {
706 if (argc == 2 || af->fops->writer(af, "\n", 1) == 1) {
707 return JIM_OK;
710 JimAioSetError(interp, af->filename);
711 return JIM_ERR;
714 static int aio_cmd_isatty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
716 #ifdef HAVE_ISATTY
717 AioFile *af = Jim_CmdPrivData(interp);
718 Jim_SetResultInt(interp, isatty(fileno(af->fp)));
719 #else
720 Jim_SetResultInt(interp, 0);
721 #endif
723 return JIM_OK;
726 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
727 static int aio_cmd_recvfrom(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
729 AioFile *af = Jim_CmdPrivData(interp);
730 char *buf;
731 union sockaddr_any sa;
732 long len;
733 socklen_t salen = sizeof(sa);
734 int rlen;
736 if (Jim_GetLong(interp, argv[0], &len) != JIM_OK) {
737 return JIM_ERR;
740 buf = Jim_Alloc(len + 1);
742 rlen = recvfrom(fileno(af->fp), buf, len, 0, &sa.sa, &salen);
743 if (rlen < 0) {
744 Jim_Free(buf);
745 JimAioSetError(interp, NULL);
746 return JIM_ERR;
748 buf[rlen] = 0;
749 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, rlen));
751 if (argc > 1) {
752 return JimFormatIpAddress(interp, argv[1], &sa);
755 return JIM_OK;
759 static int aio_cmd_sendto(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
761 AioFile *af = Jim_CmdPrivData(interp);
762 int wlen;
763 int len;
764 const char *wdata;
765 union sockaddr_any sa;
766 const char *addr = Jim_String(argv[1]);
767 int salen;
769 if (IPV6 && af->addr_family == PF_INET6) {
770 if (JimParseIPv6Address(interp, addr, &sa, &salen) != JIM_OK) {
771 return JIM_ERR;
774 else if (JimParseIpAddress(interp, addr, &sa, &salen) != JIM_OK) {
775 return JIM_ERR;
777 wdata = Jim_GetString(argv[0], &wlen);
779 /* Note that we don't validate the socket type. Rely on sendto() failing if appropriate */
780 len = sendto(fileno(af->fp), wdata, wlen, 0, &sa.sa, salen);
781 if (len < 0) {
782 JimAioSetError(interp, NULL);
783 return JIM_ERR;
785 Jim_SetResultInt(interp, len);
786 return JIM_OK;
789 static int aio_cmd_accept(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
791 AioFile *af = Jim_CmdPrivData(interp);
792 int sock;
793 union sockaddr_any sa;
794 socklen_t addrlen = sizeof(sa);
796 sock = accept(af->fd, &sa.sa, &addrlen);
797 if (sock < 0) {
798 JimAioSetError(interp, NULL);
799 return JIM_ERR;
802 if (argc > 0) {
803 if (JimFormatIpAddress(interp, argv[0], &sa) != JIM_OK) {
804 return JIM_ERR;
808 /* Create the file command */
809 return JimMakeChannel(interp, NULL, sock, Jim_NewStringObj(interp, "accept", -1),
810 "aio.sockstream%ld", af->addr_family, "r+") ? JIM_OK : JIM_ERR;
813 static int aio_cmd_listen(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
815 AioFile *af = Jim_CmdPrivData(interp);
816 long backlog;
818 if (Jim_GetLong(interp, argv[0], &backlog) != JIM_OK) {
819 return JIM_ERR;
822 if (listen(af->fd, backlog)) {
823 JimAioSetError(interp, NULL);
824 return JIM_ERR;
827 return JIM_OK;
829 #endif /* JIM_BOOTSTRAP */
831 static int aio_cmd_flush(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
833 AioFile *af = Jim_CmdPrivData(interp);
835 if (fflush(af->fp) == EOF) {
836 JimAioSetError(interp, af->filename);
837 return JIM_ERR;
839 return JIM_OK;
842 static int aio_cmd_eof(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
844 AioFile *af = Jim_CmdPrivData(interp);
846 Jim_SetResultInt(interp, feof(af->fp));
847 return JIM_OK;
850 static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
852 if (argc == 3) {
853 #if !defined(JIM_ANSIC) && defined(HAVE_SHUTDOWN)
854 static const char * const options[] = { "r", "w", NULL };
855 enum { OPT_R, OPT_W, };
856 int option;
857 AioFile *af = Jim_CmdPrivData(interp);
859 if (Jim_GetEnum(interp, argv[2], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
860 return JIM_ERR;
862 if (shutdown(af->fd, option == OPT_R ? SHUT_RD : SHUT_WR) == 0) {
863 return JIM_OK;
865 JimAioSetError(interp, NULL);
866 #else
867 Jim_SetResultString(interp, "async close not supported", -1);
868 #endif
869 return JIM_ERR;
872 return Jim_DeleteCommand(interp, Jim_String(argv[0]));
875 static int aio_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
877 AioFile *af = Jim_CmdPrivData(interp);
878 int orig = SEEK_SET;
879 jim_wide offset;
881 if (argc == 2) {
882 if (Jim_CompareStringImmediate(interp, argv[1], "start"))
883 orig = SEEK_SET;
884 else if (Jim_CompareStringImmediate(interp, argv[1], "current"))
885 orig = SEEK_CUR;
886 else if (Jim_CompareStringImmediate(interp, argv[1], "end"))
887 orig = SEEK_END;
888 else {
889 return -1;
892 if (Jim_GetWide(interp, argv[0], &offset) != JIM_OK) {
893 return JIM_ERR;
895 if (fseeko(af->fp, offset, orig) == -1) {
896 JimAioSetError(interp, af->filename);
897 return JIM_ERR;
899 return JIM_OK;
902 static int aio_cmd_tell(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
904 AioFile *af = Jim_CmdPrivData(interp);
906 Jim_SetResultInt(interp, ftello(af->fp));
907 return JIM_OK;
910 static int aio_cmd_filename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
912 AioFile *af = Jim_CmdPrivData(interp);
914 Jim_SetResult(interp, af->filename);
915 return JIM_OK;
918 #ifdef O_NDELAY
919 static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
921 AioFile *af = Jim_CmdPrivData(interp);
923 int fmode = fcntl(af->fd, F_GETFL);
925 if (argc) {
926 long nb;
928 if (Jim_GetLong(interp, argv[0], &nb) != JIM_OK) {
929 return JIM_ERR;
931 if (nb) {
932 fmode |= O_NDELAY;
934 else {
935 fmode &= ~O_NDELAY;
937 (void)fcntl(af->fd, F_SETFL, fmode);
939 Jim_SetResultInt(interp, (fmode & O_NONBLOCK) ? 1 : 0);
940 return JIM_OK;
942 #endif
944 #ifdef HAVE_FSYNC
945 static int aio_cmd_sync(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
947 AioFile *af = Jim_CmdPrivData(interp);
949 fflush(af->fp);
950 fsync(af->fd);
951 return JIM_OK;
953 #endif
955 static int aio_cmd_buffering(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
957 AioFile *af = Jim_CmdPrivData(interp);
959 static const char * const options[] = {
960 "none",
961 "line",
962 "full",
963 NULL
965 enum
967 OPT_NONE,
968 OPT_LINE,
969 OPT_FULL,
971 int option;
973 if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
974 return JIM_ERR;
976 switch (option) {
977 case OPT_NONE:
978 setvbuf(af->fp, NULL, _IONBF, 0);
979 break;
980 case OPT_LINE:
981 setvbuf(af->fp, NULL, _IOLBF, BUFSIZ);
982 break;
983 case OPT_FULL:
984 setvbuf(af->fp, NULL, _IOFBF, BUFSIZ);
985 break;
987 return JIM_OK;
990 #ifdef jim_ext_eventloop
991 static void JimAioFileEventFinalizer(Jim_Interp *interp, void *clientData)
993 Jim_Obj **objPtrPtr = clientData;
995 Jim_DecrRefCount(interp, *objPtrPtr);
996 *objPtrPtr = NULL;
999 static int JimAioFileEventHandler(Jim_Interp *interp, void *clientData, int mask)
1001 Jim_Obj **objPtrPtr = clientData;
1003 return Jim_EvalObjBackground(interp, *objPtrPtr);
1006 static int aio_eventinfo(Jim_Interp *interp, AioFile * af, unsigned mask, Jim_Obj **scriptHandlerObj,
1007 int argc, Jim_Obj * const *argv)
1009 if (argc == 0) {
1010 /* Return current script */
1011 if (*scriptHandlerObj) {
1012 Jim_SetResult(interp, *scriptHandlerObj);
1014 return JIM_OK;
1017 if (*scriptHandlerObj) {
1018 /* Delete old handler */
1019 Jim_DeleteFileHandler(interp, af->fd, mask);
1022 /* Now possibly add the new script(s) */
1023 if (Jim_Length(argv[0]) == 0) {
1024 /* Empty script, so done */
1025 return JIM_OK;
1028 /* A new script to add */
1029 Jim_IncrRefCount(argv[0]);
1030 *scriptHandlerObj = argv[0];
1032 Jim_CreateFileHandler(interp, af->fd, mask,
1033 JimAioFileEventHandler, scriptHandlerObj, JimAioFileEventFinalizer);
1035 return JIM_OK;
1038 static int aio_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1040 AioFile *af = Jim_CmdPrivData(interp);
1042 return aio_eventinfo(interp, af, JIM_EVENT_READABLE, &af->rEvent, argc, argv);
1045 static int aio_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1047 AioFile *af = Jim_CmdPrivData(interp);
1049 return aio_eventinfo(interp, af, JIM_EVENT_WRITABLE, &af->wEvent, argc, argv);
1052 static int aio_cmd_onexception(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1054 AioFile *af = Jim_CmdPrivData(interp);
1056 return aio_eventinfo(interp, af, JIM_EVENT_EXCEPTION, &af->eEvent, argc, argv);
1058 #endif
1060 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1061 static int aio_cmd_ssl(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1063 AioFile *af = Jim_CmdPrivData(interp);
1064 SSL *ssl;
1065 SSL_CTX *ssl_ctx;
1066 int fd, server = 0;
1068 if (argc == 5) {
1069 if (!Jim_CompareStringImmediate(interp, argv[2], "-server")) {
1070 return JIM_ERR;
1072 server = 1;
1074 else if (argc != 2) {
1075 Jim_WrongNumArgs(interp, 2, argv, "?-server cert priv?");
1076 return JIM_ERR;
1079 fd = fileno(af->fp);
1080 #if defined(HAVE_DUP)
1081 fd = dup(fd);
1082 if (fd < 0) {
1083 return JIM_ERR;
1085 #endif
1086 ssl_ctx = JimAioSslCtx(interp);
1087 if (ssl_ctx == NULL) {
1088 return JIM_ERR;
1091 ssl = SSL_new(ssl_ctx);
1092 if (ssl == NULL) {
1093 #if defined(HAVE_DUP)
1094 close(fd);
1095 #endif
1096 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1097 return JIM_ERR;
1100 SSL_set_cipher_list(ssl, "ALL");
1102 if (SSL_set_fd(ssl, fileno(af->fp)) == 0) {
1103 goto out;
1106 if (server) {
1107 if (SSL_use_certificate_file(ssl, Jim_String(argv[3]), SSL_FILETYPE_PEM) != 1) {
1108 goto out;
1111 if (SSL_use_PrivateKey_file(ssl, Jim_String(argv[4]), SSL_FILETYPE_PEM) != 1) {
1112 goto out;
1115 if (SSL_accept(ssl) != 1) {
1116 goto out;
1119 else {
1120 if (SSL_connect(ssl) != 1) {
1121 goto out;
1125 af = JimMakeChannel(interp, NULL, fd, NULL, "aio.sslstream%ld", af->addr_family, "r+");
1126 if (af == NULL) {
1127 goto out;
1130 af->ssl = ssl;
1131 af->fops = &ssl_fops;
1133 return JIM_OK;
1135 out:
1136 #if defined(HAVE_DUP)
1137 close(fd);
1138 #endif
1139 SSL_free(ssl);
1140 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1141 return JIM_ERR;
1144 static int aio_cmd_verify(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1146 AioFile *af = Jim_CmdPrivData(interp);
1147 int ret;
1149 if (!af->fops->verify) {
1150 return JIM_OK;
1153 ret = af->fops->verify(af);
1154 if (ret != JIM_OK) {
1155 if (JimCheckStreamError(interp, af)) {
1156 JimAioSetError(interp, af->filename);
1157 } else {
1158 Jim_SetResultString(interp, "failed to verify the connection authenticity", -1);
1161 return ret;
1163 #endif /* JIM_BOOTSTRAP */
1165 #if defined(HAVE_STRUCT_FLOCK) && !defined(JIM_BOOTSTRAP)
1166 static int aio_cmd_lock(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1168 AioFile *af = Jim_CmdPrivData(interp);
1169 struct flock fl;
1171 fl.l_start = 0;
1172 fl.l_len = 0;
1173 fl.l_type = F_WRLCK;
1174 fl.l_whence = SEEK_SET;
1176 switch (fcntl(af->fd, F_SETLK, &fl))
1178 case 0:
1179 Jim_SetResultInt(interp, 1);
1180 break;
1181 case -1:
1182 if (errno == EACCES || errno == EAGAIN)
1183 Jim_SetResultInt(interp, 0);
1184 else
1186 Jim_SetResultFormatted(interp, "lock failed: %s",
1187 strerror(errno));
1188 return JIM_ERR;
1190 break;
1191 default:
1192 Jim_SetResultInt(interp, 0);
1193 break;
1196 return JIM_OK;
1199 static int aio_cmd_unlock(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1201 AioFile *af = Jim_CmdPrivData(interp);
1202 struct flock fl;
1203 fl.l_start = 0;
1204 fl.l_len = 0;
1205 fl.l_type = F_UNLCK;
1206 fl.l_whence = SEEK_SET;
1208 Jim_SetResultInt(interp, fcntl(af->fd, F_SETLK, &fl) == 0);
1209 return JIM_OK;
1211 #endif /* JIM_BOOTSTRAP */
1213 #if defined(HAVE_TERMIOS_H) && !defined(JIM_BOOTSTRAP)
1214 static int aio_cmd_tty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1216 AioFile *af = Jim_CmdPrivData(interp);
1217 Jim_Obj *dictObjPtr;
1218 int ret;
1220 if (argc == 0) {
1221 /* get the current settings as a dictionary */
1222 dictObjPtr = Jim_GetTtySettings(interp, af->fd);
1223 if (dictObjPtr == NULL) {
1224 JimAioSetError(interp, NULL);
1225 return JIM_ERR;
1227 Jim_SetResult(interp, dictObjPtr);
1228 return JIM_OK;
1231 if (argc > 1) {
1232 /* Convert name value arguments to a dictionary */
1233 dictObjPtr = Jim_NewListObj(interp, argv, argc);
1235 else {
1236 /* The settings are already given as a list */
1237 dictObjPtr = argv[0];
1239 Jim_IncrRefCount(dictObjPtr);
1241 if (Jim_ListLength(interp, dictObjPtr) % 2) {
1242 /* Must be a valid dictionary */
1243 Jim_DecrRefCount(interp, dictObjPtr);
1244 return -1;
1247 ret = Jim_SetTtySettings(interp, af->fd, dictObjPtr);
1248 if (ret < 0) {
1249 JimAioSetError(interp, NULL);
1250 ret = JIM_ERR;
1252 Jim_DecrRefCount(interp, dictObjPtr);
1254 return ret;
1256 #endif /* JIM_BOOTSTRAP */
1258 static const jim_subcmd_type aio_command_table[] = {
1259 { "read",
1260 "?-nonewline? ?len?",
1261 aio_cmd_read,
1264 /* Description: Read and return bytes from the stream. To eof if no len. */
1266 { "copyto",
1267 "handle ?size?",
1268 aio_cmd_copy,
1271 /* Description: Copy up to 'size' bytes to the given filehandle, or to eof if no size. */
1273 { "gets",
1274 "?var?",
1275 aio_cmd_gets,
1278 /* Description: Read one line and return it or store it in the var */
1280 { "puts",
1281 "?-nonewline? str",
1282 aio_cmd_puts,
1285 /* Description: Write the string, with newline unless -nonewline */
1287 { "isatty",
1288 NULL,
1289 aio_cmd_isatty,
1292 /* Description: Is the file descriptor a tty? */
1294 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
1295 { "recvfrom",
1296 "len ?addrvar?",
1297 aio_cmd_recvfrom,
1300 /* Description: Receive up to 'len' bytes on the socket. Sets 'addrvar' with receive address, if set */
1302 { "sendto",
1303 "str address",
1304 aio_cmd_sendto,
1307 /* Description: Send 'str' to the given address (dgram only) */
1309 { "accept",
1310 "?addrvar?",
1311 aio_cmd_accept,
1314 /* Description: Server socket only: Accept a connection and return stream */
1316 { "listen",
1317 "backlog",
1318 aio_cmd_listen,
1321 /* Description: Set the listen backlog for server socket */
1323 #endif /* JIM_BOOTSTRAP */
1324 { "flush",
1325 NULL,
1326 aio_cmd_flush,
1329 /* Description: Flush the stream */
1331 { "eof",
1332 NULL,
1333 aio_cmd_eof,
1336 /* Description: Returns 1 if stream is at eof */
1338 { "close",
1339 "?r(ead)|w(rite)?",
1340 aio_cmd_close,
1343 JIM_MODFLAG_FULLARGV,
1344 /* Description: Closes the stream. */
1346 { "seek",
1347 "offset ?start|current|end",
1348 aio_cmd_seek,
1351 /* Description: Seeks in the stream (default 'current') */
1353 { "tell",
1354 NULL,
1355 aio_cmd_tell,
1358 /* Description: Returns the current seek position */
1360 { "filename",
1361 NULL,
1362 aio_cmd_filename,
1365 /* Description: Returns the original filename */
1367 #ifdef O_NDELAY
1368 { "ndelay",
1369 "?0|1?",
1370 aio_cmd_ndelay,
1373 /* Description: Set O_NDELAY (if arg). Returns current/new setting. */
1375 #endif
1376 #ifdef HAVE_FSYNC
1377 { "sync",
1378 NULL,
1379 aio_cmd_sync,
1382 /* Description: Flush and fsync() the stream */
1384 #endif
1385 { "buffering",
1386 "none|line|full",
1387 aio_cmd_buffering,
1390 /* Description: Sets buffering */
1392 #ifdef jim_ext_eventloop
1393 { "readable",
1394 "?readable-script?",
1395 aio_cmd_readable,
1398 /* Description: Returns script, or invoke readable-script when readable, {} to remove */
1400 { "writable",
1401 "?writable-script?",
1402 aio_cmd_writable,
1405 /* Description: Returns script, or invoke writable-script when writable, {} to remove */
1407 { "onexception",
1408 "?exception-script?",
1409 aio_cmd_onexception,
1412 /* Description: Returns script, or invoke exception-script when oob data, {} to remove */
1414 #endif
1415 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1416 { "ssl",
1417 "?-server cert priv?",
1418 aio_cmd_ssl,
1421 JIM_MODFLAG_FULLARGV
1422 /* Description: Wraps a stream socket with SSL/TLS and returns a new channel */
1424 { "verify",
1425 NULL,
1426 aio_cmd_verify,
1429 /* Description: Verifies the certificate of a SSL/TLS channel */
1431 #endif /* JIM_BOOTSTRAP */
1432 #if defined(HAVE_STRUCT_FLOCK) && !defined(JIM_BOOTSTRAP)
1433 { "lock",
1434 NULL,
1435 aio_cmd_lock,
1438 /* Description: Attempt to get a lock. */
1440 { "unlock",
1441 NULL,
1442 aio_cmd_unlock,
1445 /* Description: Relase a lock. */
1447 #endif /* JIM_BOOTSTRAP */
1448 #if defined(HAVE_TERMIOS_H) && !defined(JIM_BOOTSTRAP)
1449 { "tty",
1450 "?baud rate? ?data bits? ?stop bits? ?parity even|odd|none? ?handshake xonxoff|rtscts|none? ?input raw|cooked? ?output raw|cooked? ?vmin n? ?vtime n?",
1451 aio_cmd_tty,
1454 /* Description: Get or set tty settings - valid only on a tty */
1456 #endif /* JIM_BOOTSTRAP */
1457 { NULL }
1460 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1462 return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, aio_command_table, argc, argv), argc, argv);
1465 static int JimAioOpenCommand(Jim_Interp *interp, int argc,
1466 Jim_Obj *const *argv)
1468 const char *mode;
1470 if (argc != 2 && argc != 3) {
1471 Jim_WrongNumArgs(interp, 1, argv, "filename ?mode?");
1472 return JIM_ERR;
1475 mode = (argc == 3) ? Jim_String(argv[2]) : "r";
1477 #ifdef jim_ext_tclcompat
1479 const char *filename = Jim_String(argv[1]);
1481 /* If the filename starts with '|', use popen instead */
1482 if (*filename == '|') {
1483 Jim_Obj *evalObj[3];
1485 evalObj[0] = Jim_NewStringObj(interp, "::popen", -1);
1486 evalObj[1] = Jim_NewStringObj(interp, filename + 1, -1);
1487 evalObj[2] = Jim_NewStringObj(interp, mode, -1);
1489 return Jim_EvalObjVector(interp, 3, evalObj);
1492 #endif
1493 return JimMakeChannel(interp, NULL, -1, argv[1], "aio.handle%ld", 0, mode) ? JIM_OK : JIM_ERR;
1496 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1497 static void JimAioSslContextDelProc(struct Jim_Interp *interp, void *privData)
1499 SSL_CTX_free((SSL_CTX *)privData);
1500 ERR_free_strings();
1503 static SSL_CTX *JimAioSslCtx(Jim_Interp *interp)
1505 SSL_CTX *ssl_ctx = (SSL_CTX *)Jim_GetAssocData(interp, "ssl_ctx");
1506 if (ssl_ctx == NULL) {
1507 SSL_load_error_strings();
1508 SSL_library_init();
1509 ssl_ctx = SSL_CTX_new(TLSv1_2_method());
1510 if (ssl_ctx && SSL_CTX_set_default_verify_paths(ssl_ctx)) {
1511 SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
1512 Jim_SetAssocData(interp, "ssl_ctx", JimAioSslContextDelProc, ssl_ctx);
1513 } else {
1514 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1517 return ssl_ctx;
1519 #endif /* JIM_BOOTSTRAP */
1522 * Creates a channel for fh/fd/filename.
1524 * If fh is not NULL, uses that as the channel (and sets AIO_KEEPOPEN).
1525 * Otherwise, if fd is >= 0, uses that as the channel.
1526 * Otherwise opens 'filename' with mode 'mode'.
1528 * hdlfmt is a sprintf format for the filehandle. Anything with %ld at the end will do.
1529 * mode is used for open or fdopen.
1531 * Creates the command and sets the name as the current result.
1532 * Returns the AioFile pointer on sucess or NULL on failure.
1534 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
1535 const char *hdlfmt, int family, const char *mode)
1537 AioFile *af;
1538 char buf[AIO_CMD_LEN];
1539 int openFlags = 0;
1541 snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
1543 if (fh) {
1544 openFlags = AIO_KEEPOPEN;
1547 snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
1548 if (!filename) {
1549 filename = Jim_NewStringObj(interp, buf, -1);
1552 Jim_IncrRefCount(filename);
1554 if (fh == NULL) {
1555 #if !defined(JIM_ANSIC)
1556 if (fd >= 0) {
1557 fh = fdopen(fd, mode);
1559 else
1560 #endif
1561 fh = fopen(Jim_String(filename), mode);
1563 if (fh == NULL) {
1564 JimAioSetError(interp, filename);
1565 #if !defined(JIM_ANSIC)
1566 if (fd >= 0) {
1567 close(fd);
1569 #endif
1570 Jim_DecrRefCount(interp, filename);
1571 return NULL;
1575 /* Create the file command */
1576 af = Jim_Alloc(sizeof(*af));
1577 memset(af, 0, sizeof(*af));
1578 af->fp = fh;
1579 af->fd = fileno(fh);
1580 af->filename = filename;
1581 #ifdef FD_CLOEXEC
1582 if ((openFlags & AIO_KEEPOPEN) == 0) {
1583 (void)fcntl(af->fd, F_SETFD, FD_CLOEXEC);
1585 #endif
1586 af->openFlags = openFlags;
1587 af->addr_family = family;
1588 af->fops = &stdio_fops;
1589 af->ssl = NULL;
1591 Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc);
1593 /* Note that the command must use the global namespace, even if
1594 * the current namespace is something different
1596 Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, Jim_NewStringObj(interp, buf, -1)));
1598 return af;
1601 #if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && defined(HAVE_SYS_UN_H))
1603 * Create a pair of channels. e.g. from pipe() or socketpair()
1605 static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename,
1606 const char *hdlfmt, int family, const char *mode[2])
1608 if (JimMakeChannel(interp, NULL, p[0], filename, hdlfmt, family, mode[0])) {
1609 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
1610 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
1612 if (JimMakeChannel(interp, NULL, p[1], filename, hdlfmt, family, mode[1])) {
1613 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
1614 Jim_SetResult(interp, objPtr);
1615 return JIM_OK;
1619 /* Can only be here if fdopen() failed */
1620 close(p[0]);
1621 close(p[1]);
1622 JimAioSetError(interp, NULL);
1623 return JIM_ERR;
1625 #endif
1627 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
1629 static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1631 const char *hdlfmt = "aio.unknown%ld";
1632 const char *socktypes[] = {
1633 "unix",
1634 "unix.server",
1635 "dgram",
1636 "dgram.server",
1637 "stream",
1638 "stream.server",
1639 "pipe",
1640 "pair",
1641 NULL
1643 enum
1645 SOCK_UNIX,
1646 SOCK_UNIX_SERVER,
1647 SOCK_DGRAM_CLIENT,
1648 SOCK_DGRAM_SERVER,
1649 SOCK_STREAM_CLIENT,
1650 SOCK_STREAM_SERVER,
1651 SOCK_STREAM_PIPE,
1652 SOCK_STREAM_SOCKETPAIR,
1654 int socktype;
1655 int sock;
1656 const char *hostportarg = NULL;
1657 int res;
1658 int on = 1;
1659 const char *mode = "r+";
1660 int family = PF_INET;
1661 Jim_Obj *argv0 = argv[0];
1662 int ipv6 = 0;
1664 if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-ipv6")) {
1665 if (!IPV6) {
1666 Jim_SetResultString(interp, "ipv6 not supported", -1);
1667 return JIM_ERR;
1669 ipv6 = 1;
1670 family = PF_INET6;
1672 argc -= ipv6;
1673 argv += ipv6;
1675 if (argc < 2) {
1676 wrongargs:
1677 Jim_WrongNumArgs(interp, 1, &argv0, "?-ipv6? type ?address?");
1678 return JIM_ERR;
1681 if (Jim_GetEnum(interp, argv[1], socktypes, &socktype, "socket type", JIM_ERRMSG) != JIM_OK)
1682 return JIM_ERR;
1684 Jim_SetEmptyResult(interp);
1686 hdlfmt = "aio.sock%ld";
1688 if (argc > 2) {
1689 hostportarg = Jim_String(argv[2]);
1692 switch (socktype) {
1693 case SOCK_DGRAM_CLIENT:
1694 if (argc == 2) {
1695 /* No address, so an unconnected dgram socket */
1696 sock = socket(family, SOCK_DGRAM, 0);
1697 if (sock < 0) {
1698 JimAioSetError(interp, NULL);
1699 return JIM_ERR;
1701 break;
1703 /* fall through */
1704 case SOCK_STREAM_CLIENT:
1706 union sockaddr_any sa;
1707 int salen;
1709 if (argc != 3) {
1710 goto wrongargs;
1713 if (ipv6) {
1714 if (JimParseIPv6Address(interp, hostportarg, &sa, &salen) != JIM_OK) {
1715 return JIM_ERR;
1718 else if (JimParseIpAddress(interp, hostportarg, &sa, &salen) != JIM_OK) {
1719 return JIM_ERR;
1721 sock = socket(family, (socktype == SOCK_DGRAM_CLIENT) ? SOCK_DGRAM : SOCK_STREAM, 0);
1722 if (sock < 0) {
1723 JimAioSetError(interp, NULL);
1724 return JIM_ERR;
1726 res = connect(sock, &sa.sa, salen);
1727 if (res) {
1728 JimAioSetError(interp, argv[2]);
1729 close(sock);
1730 return JIM_ERR;
1733 break;
1735 case SOCK_STREAM_SERVER:
1736 case SOCK_DGRAM_SERVER:
1738 union sockaddr_any sa;
1739 int salen;
1741 if (argc != 3) {
1742 goto wrongargs;
1745 if (ipv6) {
1746 if (JimParseIPv6Address(interp, hostportarg, &sa, &salen) != JIM_OK) {
1747 return JIM_ERR;
1750 else if (JimParseIpAddress(interp, hostportarg, &sa, &salen) != JIM_OK) {
1751 return JIM_ERR;
1753 sock = socket(family, (socktype == SOCK_DGRAM_SERVER) ? SOCK_DGRAM : SOCK_STREAM, 0);
1754 if (sock < 0) {
1755 JimAioSetError(interp, NULL);
1756 return JIM_ERR;
1759 /* Enable address reuse */
1760 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
1762 res = bind(sock, &sa.sa, salen);
1763 if (res) {
1764 JimAioSetError(interp, argv[2]);
1765 close(sock);
1766 return JIM_ERR;
1768 if (socktype == SOCK_STREAM_SERVER) {
1769 res = listen(sock, 5);
1770 if (res) {
1771 JimAioSetError(interp, NULL);
1772 close(sock);
1773 return JIM_ERR;
1776 hdlfmt = "aio.socksrv%ld";
1778 break;
1780 #ifdef HAVE_SYS_UN_H
1781 case SOCK_UNIX:
1783 struct sockaddr_un sa;
1784 socklen_t len;
1786 if (argc != 3 || ipv6) {
1787 goto wrongargs;
1790 if (JimParseDomainAddress(interp, hostportarg, &sa) != JIM_OK) {
1791 JimAioSetError(interp, argv[2]);
1792 return JIM_ERR;
1794 family = PF_UNIX;
1795 sock = socket(PF_UNIX, SOCK_STREAM, 0);
1796 if (sock < 0) {
1797 JimAioSetError(interp, NULL);
1798 return JIM_ERR;
1800 len = strlen(sa.sun_path) + 1 + sizeof(sa.sun_family);
1801 res = connect(sock, (struct sockaddr *)&sa, len);
1802 if (res) {
1803 JimAioSetError(interp, argv[2]);
1804 close(sock);
1805 return JIM_ERR;
1807 hdlfmt = "aio.sockunix%ld";
1808 break;
1811 case SOCK_UNIX_SERVER:
1813 struct sockaddr_un sa;
1814 socklen_t len;
1816 if (argc != 3 || ipv6) {
1817 goto wrongargs;
1820 if (JimParseDomainAddress(interp, hostportarg, &sa) != JIM_OK) {
1821 JimAioSetError(interp, argv[2]);
1822 return JIM_ERR;
1824 family = PF_UNIX;
1825 sock = socket(PF_UNIX, SOCK_STREAM, 0);
1826 if (sock < 0) {
1827 JimAioSetError(interp, NULL);
1828 return JIM_ERR;
1830 len = strlen(sa.sun_path) + 1 + sizeof(sa.sun_family);
1831 res = bind(sock, (struct sockaddr *)&sa, len);
1832 if (res) {
1833 JimAioSetError(interp, argv[2]);
1834 close(sock);
1835 return JIM_ERR;
1837 res = listen(sock, 5);
1838 if (res) {
1839 JimAioSetError(interp, NULL);
1840 close(sock);
1841 return JIM_ERR;
1843 hdlfmt = "aio.sockunixsrv%ld";
1844 break;
1846 #endif
1848 #if defined(HAVE_SOCKETPAIR) && defined(HAVE_SYS_UN_H)
1849 case SOCK_STREAM_SOCKETPAIR:
1851 int p[2];
1852 static const char *mode[2] = { "r+", "r+" };
1854 if (argc != 2 || ipv6) {
1855 goto wrongargs;
1858 if (socketpair(PF_UNIX, SOCK_STREAM, 0, p) < 0) {
1859 JimAioSetError(interp, NULL);
1860 return JIM_ERR;
1862 return JimMakeChannelPair(interp, p, argv[1], "aio.sockpair%ld", PF_UNIX, mode);
1864 break;
1865 #endif
1867 #if defined(HAVE_PIPE)
1868 case SOCK_STREAM_PIPE:
1870 int p[2];
1871 static const char *mode[2] = { "r", "w" };
1873 if (argc != 2 || ipv6) {
1874 goto wrongargs;
1877 if (pipe(p) < 0) {
1878 JimAioSetError(interp, NULL);
1879 return JIM_ERR;
1882 return JimMakeChannelPair(interp, p, argv[1], "aio.pipe%ld", 0, mode);
1884 break;
1885 #endif
1887 default:
1888 Jim_SetResultString(interp, "Unsupported socket type", -1);
1889 return JIM_ERR;
1892 return JimMakeChannel(interp, NULL, sock, argv[1], hdlfmt, family, mode) ? JIM_OK : JIM_ERR;
1894 #endif /* JIM_BOOTSTRAP */
1897 * Returns the file descriptor of a writable, newly created temp file
1898 * or -1 on error.
1900 * On success, leaves the filename in the interpreter result, otherwise
1901 * leaves an error message.
1903 int Jim_MakeTempFile(Jim_Interp *interp, const char *template)
1905 #ifdef HAVE_MKSTEMP
1906 int fd;
1907 mode_t mask;
1908 Jim_Obj *filenameObj;
1910 if (template == NULL) {
1911 const char *tmpdir = getenv("TMPDIR");
1912 if (tmpdir == NULL || *tmpdir == '\0' || access(tmpdir, W_OK) != 0) {
1913 tmpdir = "/tmp/";
1915 filenameObj = Jim_NewStringObj(interp, tmpdir, -1);
1916 if (tmpdir[0] && tmpdir[strlen(tmpdir) - 1] != '/') {
1917 Jim_AppendString(interp, filenameObj, "/", 1);
1919 Jim_AppendString(interp, filenameObj, "tcl.tmp.XXXXXX", -1);
1921 else {
1922 filenameObj = Jim_NewStringObj(interp, template, -1);
1925 /* Update the template name directly with the filename */
1926 mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
1927 fd = mkstemp(filenameObj->bytes);
1928 umask(mask);
1929 if (fd < 0) {
1930 JimAioSetError(interp, filenameObj);
1931 Jim_FreeNewObj(interp, filenameObj);
1932 return -1;
1935 Jim_SetResult(interp, filenameObj);
1936 return fd;
1937 #else
1938 Jim_SetResultString(interp, "platform has no tempfile support", -1);
1939 return -1;
1940 #endif
1943 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1944 static int JimAioLoadSSLCertsCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1946 SSL_CTX *ssl_ctx;
1948 if (argc != 2) {
1949 Jim_WrongNumArgs(interp, 1, argv, "dir");
1950 return JIM_ERR;
1953 ssl_ctx = JimAioSslCtx(interp);
1954 if (!ssl_ctx) {
1955 return JIM_ERR;
1957 if (SSL_CTX_load_verify_locations(ssl_ctx, NULL, Jim_String(argv[1])) == 1) {
1958 return JIM_OK;
1960 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1961 return JIM_ERR;
1963 #endif /* JIM_BOOTSTRAP */
1965 int Jim_aioInit(Jim_Interp *interp)
1967 if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG))
1968 return JIM_ERR;
1970 #if defined(JIM_SSL)
1971 Jim_CreateCommand(interp, "load_ssl_certs", JimAioLoadSSLCertsCommand, NULL, NULL);
1972 #endif
1974 Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL);
1975 #ifndef JIM_ANSIC
1976 Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL);
1977 #endif
1979 /* Create filehandles for stdin, stdout and stderr */
1980 JimMakeChannel(interp, stdin, -1, NULL, "stdin", 0, "r");
1981 JimMakeChannel(interp, stdout, -1, NULL, "stdout", 0, "w");
1982 JimMakeChannel(interp, stderr, -1, NULL, "stderr", 0, "w");
1984 return JIM_OK;