aio: Fix crash in aio copy
[jimtcl.git] / jim-aio.c
blob1ac3b561289aee68fb8e96bb6fd3bdce7f6331c7
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 #include <stdio.h>
43 #include <string.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #ifdef HAVE_UNISTD_H
47 #include <unistd.h>
48 #include <sys/stat.h>
49 #endif
51 #include "jim.h"
53 #if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H)
54 #include <sys/socket.h>
55 #include <netinet/in.h>
56 #include <arpa/inet.h>
57 #include <netdb.h>
58 #ifdef HAVE_SYS_UN_H
59 #include <sys/un.h>
60 #endif
61 #else
62 #define JIM_ANSIC
63 #endif
65 #if defined(JIM_SSL)
66 #include <openssl/ssl.h>
67 #include <openssl/err.h>
68 #endif
70 #include "jim-eventloop.h"
71 #include "jim-subcmd.h"
73 #define AIO_CMD_LEN 32 /* e.g. aio.handleXXXXXX */
74 #define AIO_BUF_LEN 256 /* Can keep this small and rely on stdio buffering */
76 #ifndef HAVE_FTELLO
77 #define ftello ftell
78 #endif
79 #ifndef HAVE_FSEEKO
80 #define fseeko fseek
81 #endif
83 #define AIO_KEEPOPEN 1
85 #if defined(JIM_IPV6)
86 #define IPV6 1
87 #else
88 #define IPV6 0
89 #ifndef PF_INET6
90 #define PF_INET6 0
91 #endif
92 #endif
94 #define JimCheckStreamError(interp, af) af->fops->error(af)
96 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
97 union sockaddr_any {
98 struct sockaddr sa;
99 struct sockaddr_in sin;
100 #if IPV6
101 struct sockaddr_in6 sin6;
102 #endif
105 #ifndef HAVE_INET_NTOP
106 const char *inet_ntop(int af, const void *src, char *dst, int size)
108 if (af != PF_INET) {
109 return NULL;
111 snprintf(dst, size, "%s", inet_ntoa(((struct sockaddr_in *)src)->sin_addr));
112 return dst;
114 #endif
115 #endif /* JIM_BOOTSTRAP */
117 struct AioFile;
119 typedef struct {
120 int (*writer)(struct AioFile *af, const char *buf, int len);
121 int (*reader)(struct AioFile *af, char *buf, int len);
122 const char *(*getline)(struct AioFile *af, char *buf, int len);
123 int (*error)(const struct AioFile *af);
124 const char *(*strerror)(struct AioFile *af);
125 int (*verify)(struct AioFile *af);
126 } JimAioFopsType;
128 typedef struct AioFile
130 FILE *fp;
131 Jim_Obj *filename;
132 int type;
133 int openFlags; /* AIO_KEEPOPEN? keep FILE* */
134 int fd;
135 Jim_Obj *rEvent;
136 Jim_Obj *wEvent;
137 Jim_Obj *eEvent;
138 int addr_family;
139 void *ssl;
140 const JimAioFopsType *fops;
141 } AioFile;
143 static int stdio_writer(struct AioFile *af, const char *buf, int len)
145 return fwrite(buf, 1, len, af->fp);
148 static int stdio_reader(struct AioFile *af, char *buf, int len)
150 return fread(buf, 1, len, af->fp);
153 static const char *stdio_getline(struct AioFile *af, char *buf, int len)
155 return fgets(buf, len, af->fp);
158 static int stdio_error(const AioFile *af)
160 if (!ferror(af->fp)) {
161 return JIM_OK;
163 clearerr(af->fp);
164 /* EAGAIN and similar are not error conditions. Just treat them like eof */
165 if (feof(af->fp) || errno == EAGAIN || errno == EINTR) {
166 return JIM_OK;
168 #ifdef ECONNRESET
169 if (errno == ECONNRESET) {
170 return JIM_OK;
172 #endif
173 #ifdef ECONNABORTED
174 if (errno != ECONNABORTED) {
175 return JIM_OK;
177 #endif
178 return JIM_ERR;
181 static const char *stdio_strerror(struct AioFile *af)
183 return strerror(errno);
186 static const JimAioFopsType stdio_fops = {
187 stdio_writer,
188 stdio_reader,
189 stdio_getline,
190 stdio_error,
191 stdio_strerror,
192 NULL
195 #if defined(JIM_SSL)
197 static SSL_CTX *JimAioSslCtx(Jim_Interp *interp);
199 static int ssl_writer(struct AioFile *af, const char *buf, int len)
201 return SSL_write(af->ssl, buf, len);
204 static int ssl_reader(struct AioFile *af, char *buf, int len)
206 return SSL_read(af->ssl, buf, len);
209 static const char *ssl_getline(struct AioFile *af, char *buf, int len)
211 int i;
212 for (i = 0; i < len + 1; i++) {
213 if (SSL_read(af->ssl, &buf[i], 1) != 1) {
214 if (i == 0) {
215 return NULL;
217 break;
219 if (buf[i] == '\n') {
220 break;
223 buf[i] = '\0';
224 return buf;
227 static int ssl_error(const struct AioFile *af)
229 if (ERR_peek_error() == 0) {
230 return JIM_OK;
233 return JIM_ERR;
236 static const char *ssl_strerror(struct AioFile *af)
238 int err = ERR_get_error();
240 if (err) {
241 return ERR_error_string(err, NULL);
244 /* should not happen */
245 return "unknown SSL error";
248 static int ssl_verify(struct AioFile *af)
250 X509 *cert;
252 cert = SSL_get_peer_certificate(af->ssl);
253 if (!cert) {
254 return JIM_ERR;
256 X509_free(cert);
258 if (SSL_get_verify_result(af->ssl) == X509_V_OK) {
259 return JIM_OK;
262 return JIM_ERR;
265 static const JimAioFopsType ssl_fops = {
266 ssl_writer,
267 ssl_reader,
268 ssl_getline,
269 ssl_error,
270 ssl_strerror,
271 ssl_verify
273 #endif
275 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
276 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
277 const char *hdlfmt, int family, const char *mode);
279 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
280 static int JimParseIPv6Address(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, int *salen)
282 #if IPV6
284 * An IPv6 addr/port looks like:
285 * [::1]
286 * [::1]:2000
287 * [fe80::223:6cff:fe95:bdc0%en1]:2000
288 * [::]:2000
289 * 2000
291 * Note that the "any" address is ::, which is the same as when no address is specified.
293 char *sthost = NULL;
294 const char *stport;
295 int ret = JIM_OK;
296 struct addrinfo req;
297 struct addrinfo *ai;
299 stport = strrchr(hostport, ':');
300 if (!stport) {
301 /* No : so, the whole thing is the port */
302 stport = hostport;
303 hostport = "::";
304 sthost = Jim_StrDup(hostport);
306 else {
307 stport++;
310 if (*hostport == '[') {
311 /* This is a numeric ipv6 address */
312 char *pt = strchr(++hostport, ']');
313 if (pt) {
314 sthost = Jim_StrDupLen(hostport, pt - hostport);
318 if (!sthost) {
319 sthost = Jim_StrDupLen(hostport, stport - hostport - 1);
322 memset(&req, '\0', sizeof(req));
323 req.ai_family = PF_INET6;
325 if (getaddrinfo(sthost, NULL, &req, &ai)) {
326 Jim_SetResultFormatted(interp, "Not a valid address: %s", hostport);
327 ret = JIM_ERR;
329 else {
330 memcpy(&sa->sin, ai->ai_addr, ai->ai_addrlen);
331 *salen = ai->ai_addrlen;
333 sa->sin.sin_port = htons(atoi(stport));
335 freeaddrinfo(ai);
337 Jim_Free(sthost);
339 return ret;
340 #else
341 Jim_SetResultString(interp, "ipv6 not supported", -1);
342 return JIM_ERR;
343 #endif
346 static int JimParseIpAddress(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, int *salen)
348 /* An IPv4 addr/port looks like:
349 * 192.168.1.5
350 * 192.168.1.5:2000
351 * 2000
353 * If the address is missing, INADDR_ANY is used.
354 * If the port is missing, 0 is used (only useful for server sockets).
356 char *sthost = NULL;
357 const char *stport;
358 int ret = JIM_OK;
360 stport = strrchr(hostport, ':');
361 if (!stport) {
362 /* No : so, the whole thing is the port */
363 stport = hostport;
364 sthost = Jim_StrDup("0.0.0.0");
366 else {
367 sthost = Jim_StrDupLen(hostport, stport - hostport);
368 stport++;
372 #ifdef HAVE_GETADDRINFO
373 struct addrinfo req;
374 struct addrinfo *ai;
375 memset(&req, '\0', sizeof(req));
376 req.ai_family = PF_INET;
378 if (getaddrinfo(sthost, NULL, &req, &ai)) {
379 ret = JIM_ERR;
381 else {
382 memcpy(&sa->sin, ai->ai_addr, ai->ai_addrlen);
383 *salen = ai->ai_addrlen;
384 freeaddrinfo(ai);
386 #else
387 struct hostent *he;
389 ret = JIM_ERR;
391 if ((he = gethostbyname(sthost)) != NULL) {
392 if (he->h_length == sizeof(sa->sin.sin_addr)) {
393 *salen = sizeof(sa->sin);
394 sa->sin.sin_family= he->h_addrtype;
395 memcpy(&sa->sin.sin_addr, he->h_addr, he->h_length); /* set address */
396 ret = JIM_OK;
399 #endif
401 sa->sin.sin_port = htons(atoi(stport));
403 Jim_Free(sthost);
405 if (ret != JIM_OK) {
406 Jim_SetResultFormatted(interp, "Not a valid address: %s", hostport);
409 return ret;
412 #ifdef HAVE_SYS_UN_H
413 static int JimParseDomainAddress(Jim_Interp *interp, const char *path, struct sockaddr_un *sa)
415 sa->sun_family = PF_UNIX;
416 snprintf(sa->sun_path, sizeof(sa->sun_path), "%s", path);
418 return JIM_OK;
420 #endif
423 * Format that address in 'sa' as a string and store in variable 'varObjPtr'
425 static int JimFormatIpAddress(Jim_Interp *interp, Jim_Obj *varObjPtr, const union sockaddr_any *sa)
427 /* INET6_ADDRSTRLEN is 46. Add some for [] and port */
428 char addrbuf[60];
430 #if IPV6
431 if (sa->sa.sa_family == PF_INET6) {
432 addrbuf[0] = '[';
433 /* Allow 9 for []:65535\0 */
434 inet_ntop(sa->sa.sa_family, &sa->sin6.sin6_addr, addrbuf + 1, sizeof(addrbuf) - 9);
435 snprintf(addrbuf + strlen(addrbuf), 8, "]:%d", ntohs(sa->sin.sin_port));
437 else
438 #endif
439 if (sa->sa.sa_family == PF_INET) {
440 /* Allow 7 for :65535\0 */
441 inet_ntop(sa->sa.sa_family, &sa->sin.sin_addr, addrbuf, sizeof(addrbuf) - 7);
442 snprintf(addrbuf + strlen(addrbuf), 7, ":%d", ntohs(sa->sin.sin_port));
444 else {
445 /* recvfrom still works on unix domain sockets, etc */
446 addrbuf[0] = 0;
449 return Jim_SetVariable(interp, varObjPtr, Jim_NewStringObj(interp, addrbuf, -1));
452 #endif /* JIM_BOOTSTRAP */
454 static const char *JimAioErrorString(AioFile *af)
456 if (af && af->fops)
457 return af->fops->strerror(af);
459 return strerror(errno);
462 static void JimAioSetError(Jim_Interp *interp, Jim_Obj *name)
464 AioFile *af = Jim_CmdPrivData(interp);
466 if (name) {
467 Jim_SetResultFormatted(interp, "%#s: %s", name, JimAioErrorString(af));
469 else {
470 Jim_SetResultString(interp, JimAioErrorString(af), -1);
474 static void JimAioDelProc(Jim_Interp *interp, void *privData)
476 AioFile *af = privData;
478 JIM_NOTUSED(interp);
480 Jim_DecrRefCount(interp, af->filename);
482 #ifdef jim_ext_eventloop
483 /* remove all existing EventHandlers */
484 Jim_DeleteFileHandler(interp, af->fp, JIM_EVENT_READABLE | JIM_EVENT_WRITABLE | JIM_EVENT_EXCEPTION);
485 #endif
487 #if defined(JIM_SSL)
488 if (af->ssl != NULL) {
489 SSL_free(af->ssl);
491 #endif
493 if (!(af->openFlags & AIO_KEEPOPEN)) {
494 fclose(af->fp);
497 Jim_Free(af);
500 static int aio_cmd_read(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
502 AioFile *af = Jim_CmdPrivData(interp);
503 char buf[AIO_BUF_LEN];
504 Jim_Obj *objPtr;
505 int nonewline = 0;
506 jim_wide neededLen = -1; /* -1 is "read as much as possible" */
508 if (argc && Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
509 nonewline = 1;
510 argv++;
511 argc--;
513 if (argc == 1) {
514 if (Jim_GetWide(interp, argv[0], &neededLen) != JIM_OK)
515 return JIM_ERR;
516 if (neededLen < 0) {
517 Jim_SetResultString(interp, "invalid parameter: negative len", -1);
518 return JIM_ERR;
521 else if (argc) {
522 return -1;
524 objPtr = Jim_NewStringObj(interp, NULL, 0);
525 while (neededLen != 0) {
526 int retval;
527 int readlen;
529 if (neededLen == -1) {
530 readlen = AIO_BUF_LEN;
532 else {
533 readlen = (neededLen > AIO_BUF_LEN ? AIO_BUF_LEN : neededLen);
535 retval = af->fops->reader(af, buf, readlen);
536 if (retval > 0) {
537 Jim_AppendString(interp, objPtr, buf, retval);
538 if (neededLen != -1) {
539 neededLen -= retval;
542 if (retval != readlen)
543 break;
545 /* Check for error conditions */
546 if (JimCheckStreamError(interp, af)) {
547 Jim_FreeNewObj(interp, objPtr);
548 return JIM_ERR;
550 if (nonewline) {
551 int len;
552 const char *s = Jim_GetString(objPtr, &len);
554 if (len > 0 && s[len - 1] == '\n') {
555 objPtr->length--;
556 objPtr->bytes[objPtr->length] = '\0';
559 Jim_SetResult(interp, objPtr);
560 return JIM_OK;
563 AioFile *Jim_AioFile(Jim_Interp *interp, Jim_Obj *command)
565 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, command, JIM_ERRMSG);
567 /* XXX: There ought to be a supported API for this */
568 if (cmdPtr && !cmdPtr->isproc && cmdPtr->u.native.cmdProc == JimAioSubCmdProc) {
569 return (AioFile *) cmdPtr->u.native.privData;
571 Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", command);
572 return NULL;
575 FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command)
577 AioFile *af;
579 af = Jim_AioFile(interp, command);
580 if (af == NULL) {
581 return NULL;
584 return af->fp;
587 static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
589 AioFile *af = Jim_CmdPrivData(interp);
590 jim_wide count = 0;
591 jim_wide maxlen = JIM_WIDE_MAX;
592 AioFile *outf = Jim_AioFile(interp, argv[0]);
594 if (outf == NULL) {
595 return JIM_ERR;
598 if (argc == 2) {
599 if (Jim_GetWide(interp, argv[1], &maxlen) != JIM_OK) {
600 return JIM_ERR;
604 while (count < maxlen) {
605 char ch;
607 if (af->fops->reader(af, &ch, 1) != 1) {
608 break;
610 if (outf->fops->writer(outf, &ch, 1) != 1) {
611 break;
613 count++;
616 if (JimCheckStreamError(interp, af) || JimCheckStreamError(interp, outf)) {
617 return JIM_ERR;
620 Jim_SetResultInt(interp, count);
622 return JIM_OK;
625 static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
627 AioFile *af = Jim_CmdPrivData(interp);
628 char buf[AIO_BUF_LEN];
629 Jim_Obj *objPtr;
630 int len;
632 errno = 0;
634 objPtr = Jim_NewStringObj(interp, NULL, 0);
635 while (1) {
636 buf[AIO_BUF_LEN - 1] = '_';
638 if (af->fops->getline(af, buf, AIO_BUF_LEN) == NULL)
639 break;
641 if (buf[AIO_BUF_LEN - 1] == '\0' && buf[AIO_BUF_LEN - 2] != '\n') {
642 Jim_AppendString(interp, objPtr, buf, AIO_BUF_LEN - 1);
644 else {
645 len = strlen(buf);
647 if (len && (buf[len - 1] == '\n')) {
648 /* strip "\n" */
649 len--;
652 Jim_AppendString(interp, objPtr, buf, len);
653 break;
657 if (JimCheckStreamError(interp, af)) {
658 /* I/O error */
659 Jim_FreeNewObj(interp, objPtr);
660 return JIM_ERR;
663 if (argc) {
664 if (Jim_SetVariable(interp, argv[0], objPtr) != JIM_OK) {
665 Jim_FreeNewObj(interp, objPtr);
666 return JIM_ERR;
669 len = Jim_Length(objPtr);
671 if (len == 0 && feof(af->fp)) {
672 /* On EOF returns -1 if varName was specified */
673 len = -1;
675 Jim_SetResultInt(interp, len);
677 else {
678 Jim_SetResult(interp, objPtr);
680 return JIM_OK;
683 static int aio_cmd_puts(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
685 AioFile *af = Jim_CmdPrivData(interp);
686 int wlen;
687 const char *wdata;
688 Jim_Obj *strObj;
690 if (argc == 2) {
691 if (!Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
692 return -1;
694 strObj = argv[1];
696 else {
697 strObj = argv[0];
700 wdata = Jim_GetString(strObj, &wlen);
701 if (af->fops->writer(af, wdata, wlen) == wlen) {
702 if (argc == 2 || af->fops->writer(af, "\n", 1) == 1) {
703 return JIM_OK;
706 JimAioSetError(interp, af->filename);
707 return JIM_ERR;
710 static int aio_cmd_isatty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
712 #ifdef HAVE_ISATTY
713 AioFile *af = Jim_CmdPrivData(interp);
714 Jim_SetResultInt(interp, isatty(fileno(af->fp)));
715 #else
716 Jim_SetResultInt(interp, 0);
717 #endif
719 return JIM_OK;
722 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
723 static int aio_cmd_recvfrom(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
725 AioFile *af = Jim_CmdPrivData(interp);
726 char *buf;
727 union sockaddr_any sa;
728 long len;
729 socklen_t salen = sizeof(sa);
730 int rlen;
732 if (Jim_GetLong(interp, argv[0], &len) != JIM_OK) {
733 return JIM_ERR;
736 buf = Jim_Alloc(len + 1);
738 rlen = recvfrom(fileno(af->fp), buf, len, 0, &sa.sa, &salen);
739 if (rlen < 0) {
740 Jim_Free(buf);
741 JimAioSetError(interp, NULL);
742 return JIM_ERR;
744 buf[rlen] = 0;
745 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, rlen));
747 if (argc > 1) {
748 return JimFormatIpAddress(interp, argv[1], &sa);
751 return JIM_OK;
755 static int aio_cmd_sendto(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
757 AioFile *af = Jim_CmdPrivData(interp);
758 int wlen;
759 int len;
760 const char *wdata;
761 union sockaddr_any sa;
762 const char *addr = Jim_String(argv[1]);
763 int salen;
765 if (IPV6 && af->addr_family == PF_INET6) {
766 if (JimParseIPv6Address(interp, addr, &sa, &salen) != JIM_OK) {
767 return JIM_ERR;
770 else if (JimParseIpAddress(interp, addr, &sa, &salen) != JIM_OK) {
771 return JIM_ERR;
773 wdata = Jim_GetString(argv[0], &wlen);
775 /* Note that we don't validate the socket type. Rely on sendto() failing if appropriate */
776 len = sendto(fileno(af->fp), wdata, wlen, 0, &sa.sa, salen);
777 if (len < 0) {
778 JimAioSetError(interp, NULL);
779 return JIM_ERR;
781 Jim_SetResultInt(interp, len);
782 return JIM_OK;
785 static int aio_cmd_accept(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
787 AioFile *af = Jim_CmdPrivData(interp);
788 int sock;
789 union sockaddr_any sa;
790 socklen_t addrlen = sizeof(sa);
792 sock = accept(af->fd, &sa.sa, &addrlen);
793 if (sock < 0) {
794 JimAioSetError(interp, NULL);
795 return JIM_ERR;
798 if (argc > 0) {
799 if (JimFormatIpAddress(interp, argv[0], &sa) != JIM_OK) {
800 return JIM_ERR;
804 /* Create the file command */
805 return JimMakeChannel(interp, NULL, sock, Jim_NewStringObj(interp, "accept", -1),
806 "aio.sockstream%ld", af->addr_family, "r+") ? JIM_OK : JIM_ERR;
809 static int aio_cmd_listen(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
811 AioFile *af = Jim_CmdPrivData(interp);
812 long backlog;
814 if (Jim_GetLong(interp, argv[0], &backlog) != JIM_OK) {
815 return JIM_ERR;
818 if (listen(af->fd, backlog)) {
819 JimAioSetError(interp, NULL);
820 return JIM_ERR;
823 return JIM_OK;
825 #endif /* JIM_BOOTSTRAP */
827 static int aio_cmd_flush(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
829 AioFile *af = Jim_CmdPrivData(interp);
831 if (fflush(af->fp) == EOF) {
832 JimAioSetError(interp, af->filename);
833 return JIM_ERR;
835 return JIM_OK;
838 static int aio_cmd_eof(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
840 AioFile *af = Jim_CmdPrivData(interp);
842 Jim_SetResultInt(interp, feof(af->fp));
843 return JIM_OK;
846 static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
848 if (argc == 3) {
849 #if !defined(JIM_ANSIC) && defined(HAVE_SHUTDOWN)
850 static const char * const options[] = { "r", "w", NULL };
851 enum { OPT_R, OPT_W, };
852 int option;
853 AioFile *af = Jim_CmdPrivData(interp);
855 if (Jim_GetEnum(interp, argv[2], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
856 return JIM_ERR;
858 if (shutdown(af->fd, option == OPT_R ? SHUT_RD : SHUT_WR) == 0) {
859 return JIM_OK;
861 JimAioSetError(interp, NULL);
862 #else
863 Jim_SetResultString(interp, "async close not supported", -1);
864 #endif
865 return JIM_ERR;
868 return Jim_DeleteCommand(interp, Jim_String(argv[0]));
871 static int aio_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
873 AioFile *af = Jim_CmdPrivData(interp);
874 int orig = SEEK_SET;
875 jim_wide offset;
877 if (argc == 2) {
878 if (Jim_CompareStringImmediate(interp, argv[1], "start"))
879 orig = SEEK_SET;
880 else if (Jim_CompareStringImmediate(interp, argv[1], "current"))
881 orig = SEEK_CUR;
882 else if (Jim_CompareStringImmediate(interp, argv[1], "end"))
883 orig = SEEK_END;
884 else {
885 return -1;
888 if (Jim_GetWide(interp, argv[0], &offset) != JIM_OK) {
889 return JIM_ERR;
891 if (fseeko(af->fp, offset, orig) == -1) {
892 JimAioSetError(interp, af->filename);
893 return JIM_ERR;
895 return JIM_OK;
898 static int aio_cmd_tell(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
900 AioFile *af = Jim_CmdPrivData(interp);
902 Jim_SetResultInt(interp, ftello(af->fp));
903 return JIM_OK;
906 static int aio_cmd_filename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
908 AioFile *af = Jim_CmdPrivData(interp);
910 Jim_SetResult(interp, af->filename);
911 return JIM_OK;
914 #ifdef O_NDELAY
915 static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
917 AioFile *af = Jim_CmdPrivData(interp);
919 int fmode = fcntl(af->fd, F_GETFL);
921 if (argc) {
922 long nb;
924 if (Jim_GetLong(interp, argv[0], &nb) != JIM_OK) {
925 return JIM_ERR;
927 if (nb) {
928 fmode |= O_NDELAY;
930 else {
931 fmode &= ~O_NDELAY;
933 (void)fcntl(af->fd, F_SETFL, fmode);
935 Jim_SetResultInt(interp, (fmode & O_NONBLOCK) ? 1 : 0);
936 return JIM_OK;
938 #endif
940 #ifdef HAVE_FSYNC
941 static int aio_cmd_sync(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
943 AioFile *af = Jim_CmdPrivData(interp);
945 fflush(af->fp);
946 fsync(af->fd);
947 return JIM_OK;
949 #endif
951 static int aio_cmd_buffering(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
953 AioFile *af = Jim_CmdPrivData(interp);
955 static const char * const options[] = {
956 "none",
957 "line",
958 "full",
959 NULL
961 enum
963 OPT_NONE,
964 OPT_LINE,
965 OPT_FULL,
967 int option;
969 if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
970 return JIM_ERR;
972 switch (option) {
973 case OPT_NONE:
974 setvbuf(af->fp, NULL, _IONBF, 0);
975 break;
976 case OPT_LINE:
977 setvbuf(af->fp, NULL, _IOLBF, BUFSIZ);
978 break;
979 case OPT_FULL:
980 setvbuf(af->fp, NULL, _IOFBF, BUFSIZ);
981 break;
983 return JIM_OK;
986 #ifdef jim_ext_eventloop
987 static void JimAioFileEventFinalizer(Jim_Interp *interp, void *clientData)
989 Jim_Obj **objPtrPtr = clientData;
991 Jim_DecrRefCount(interp, *objPtrPtr);
992 *objPtrPtr = NULL;
995 static int JimAioFileEventHandler(Jim_Interp *interp, void *clientData, int mask)
997 Jim_Obj **objPtrPtr = clientData;
999 return Jim_EvalObjBackground(interp, *objPtrPtr);
1002 static int aio_eventinfo(Jim_Interp *interp, AioFile * af, unsigned mask, Jim_Obj **scriptHandlerObj,
1003 int argc, Jim_Obj * const *argv)
1005 if (argc == 0) {
1006 /* Return current script */
1007 if (*scriptHandlerObj) {
1008 Jim_SetResult(interp, *scriptHandlerObj);
1010 return JIM_OK;
1013 if (*scriptHandlerObj) {
1014 /* Delete old handler */
1015 Jim_DeleteFileHandler(interp, af->fp, mask);
1018 /* Now possibly add the new script(s) */
1019 if (Jim_Length(argv[0]) == 0) {
1020 /* Empty script, so done */
1021 return JIM_OK;
1024 /* A new script to add */
1025 Jim_IncrRefCount(argv[0]);
1026 *scriptHandlerObj = argv[0];
1028 Jim_CreateFileHandler(interp, af->fp, mask,
1029 JimAioFileEventHandler, scriptHandlerObj, JimAioFileEventFinalizer);
1031 return JIM_OK;
1034 static int aio_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1036 AioFile *af = Jim_CmdPrivData(interp);
1038 return aio_eventinfo(interp, af, JIM_EVENT_READABLE, &af->rEvent, argc, argv);
1041 static int aio_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1043 AioFile *af = Jim_CmdPrivData(interp);
1045 return aio_eventinfo(interp, af, JIM_EVENT_WRITABLE, &af->wEvent, argc, argv);
1048 static int aio_cmd_onexception(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1050 AioFile *af = Jim_CmdPrivData(interp);
1052 return aio_eventinfo(interp, af, JIM_EVENT_EXCEPTION, &af->eEvent, argc, argv);
1054 #endif
1056 #if defined(JIM_SSL)
1057 static int aio_cmd_ssl(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1059 AioFile *af = Jim_CmdPrivData(interp);
1060 SSL *ssl;
1061 SSL_CTX *ssl_ctx;
1062 int fd, server = 0;
1064 if (argc == 5) {
1065 if (!Jim_CompareStringImmediate(interp, argv[2], "-server")) {
1066 return JIM_ERR;
1068 server = 1;
1070 else if (argc != 2) {
1071 Jim_WrongNumArgs(interp, 2, argv, "?-server cert priv?");
1072 return JIM_ERR;
1075 fd = fileno(af->fp);
1076 #if defined(HAVE_DUP)
1077 fd = dup(fd);
1078 if (fd < 0) {
1079 return JIM_ERR;
1081 #endif
1082 ssl_ctx = JimAioSslCtx(interp);
1083 if (ssl_ctx == NULL) {
1084 return JIM_ERR;
1087 ssl = SSL_new(ssl_ctx);
1088 if (ssl == NULL) {
1089 #if defined(HAVE_DUP)
1090 close(fd);
1091 #endif
1092 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1093 return JIM_ERR;
1096 SSL_set_cipher_list(ssl, "ALL");
1098 if (SSL_set_fd(ssl, fileno(af->fp)) == 0) {
1099 goto out;
1102 if (server) {
1103 if (SSL_use_certificate_file(ssl, Jim_String(argv[3]), SSL_FILETYPE_PEM) != 1) {
1104 goto out;
1107 if (SSL_use_PrivateKey_file(ssl, Jim_String(argv[4]), SSL_FILETYPE_PEM) != 1) {
1108 goto out;
1111 if (SSL_accept(ssl) != 1) {
1112 goto out;
1115 else {
1116 if (SSL_connect(ssl) != 1) {
1117 goto out;
1121 af = JimMakeChannel(interp, NULL, fd, NULL, "aio.sslstream%ld", af->addr_family, "r+");
1122 if (af == NULL) {
1123 goto out;
1126 af->ssl = ssl;
1127 af->fops = &ssl_fops;
1129 return JIM_OK;
1131 out:
1132 #if defined(HAVE_DUP)
1133 close(fd);
1134 #endif
1135 SSL_free(ssl);
1136 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1137 return JIM_ERR;
1140 static int aio_cmd_verify(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1142 AioFile *af = Jim_CmdPrivData(interp);
1143 int ret;
1145 if (!af->fops->verify) {
1146 return JIM_OK;
1149 ret = af->fops->verify(af);
1150 if (ret != JIM_OK) {
1151 if (JimCheckStreamError(interp, af)) {
1152 JimAioSetError(interp, af->filename);
1153 } else {
1154 Jim_SetResultString(interp, "failed to verify the connection authenticity", -1);
1157 return ret;
1159 #endif
1161 static const jim_subcmd_type aio_command_table[] = {
1162 { "read",
1163 "?-nonewline? ?len?",
1164 aio_cmd_read,
1167 /* Description: Read and return bytes from the stream. To eof if no len. */
1169 { "copyto",
1170 "handle ?size?",
1171 aio_cmd_copy,
1174 /* Description: Copy up to 'size' bytes to the given filehandle, or to eof if no size. */
1176 { "gets",
1177 "?var?",
1178 aio_cmd_gets,
1181 /* Description: Read one line and return it or store it in the var */
1183 { "puts",
1184 "?-nonewline? str",
1185 aio_cmd_puts,
1188 /* Description: Write the string, with newline unless -nonewline */
1190 { "isatty",
1191 NULL,
1192 aio_cmd_isatty,
1195 /* Description: Is the file descriptor a tty? */
1197 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
1198 { "recvfrom",
1199 "len ?addrvar?",
1200 aio_cmd_recvfrom,
1203 /* Description: Receive up to 'len' bytes on the socket. Sets 'addrvar' with receive address, if set */
1205 { "sendto",
1206 "str address",
1207 aio_cmd_sendto,
1210 /* Description: Send 'str' to the given address (dgram only) */
1212 { "accept",
1213 "?addrvar?",
1214 aio_cmd_accept,
1217 /* Description: Server socket only: Accept a connection and return stream */
1219 { "listen",
1220 "backlog",
1221 aio_cmd_listen,
1224 /* Description: Set the listen backlog for server socket */
1226 #endif /* JIM_BOOTSTRAP */
1227 { "flush",
1228 NULL,
1229 aio_cmd_flush,
1232 /* Description: Flush the stream */
1234 { "eof",
1235 NULL,
1236 aio_cmd_eof,
1239 /* Description: Returns 1 if stream is at eof */
1241 { "close",
1242 "?r(ead)|w(rite)?",
1243 aio_cmd_close,
1246 JIM_MODFLAG_FULLARGV,
1247 /* Description: Closes the stream. */
1249 { "seek",
1250 "offset ?start|current|end",
1251 aio_cmd_seek,
1254 /* Description: Seeks in the stream (default 'current') */
1256 { "tell",
1257 NULL,
1258 aio_cmd_tell,
1261 /* Description: Returns the current seek position */
1263 { "filename",
1264 NULL,
1265 aio_cmd_filename,
1268 /* Description: Returns the original filename */
1270 #ifdef O_NDELAY
1271 { "ndelay",
1272 "?0|1?",
1273 aio_cmd_ndelay,
1276 /* Description: Set O_NDELAY (if arg). Returns current/new setting. */
1278 #endif
1279 #ifdef HAVE_FSYNC
1280 { "sync",
1281 NULL,
1282 aio_cmd_sync,
1285 /* Description: Flush and fsync() the stream */
1287 #endif
1288 { "buffering",
1289 "none|line|full",
1290 aio_cmd_buffering,
1293 /* Description: Sets buffering */
1295 #ifdef jim_ext_eventloop
1296 { "readable",
1297 "?readable-script?",
1298 aio_cmd_readable,
1301 /* Description: Returns script, or invoke readable-script when readable, {} to remove */
1303 { "writable",
1304 "?writable-script?",
1305 aio_cmd_writable,
1308 /* Description: Returns script, or invoke writable-script when writable, {} to remove */
1310 { "onexception",
1311 "?exception-script?",
1312 aio_cmd_onexception,
1315 /* Description: Returns script, or invoke exception-script when oob data, {} to remove */
1317 #endif
1318 #if defined(JIM_SSL)
1319 { "ssl",
1320 "?-server cert priv?",
1321 aio_cmd_ssl,
1324 JIM_MODFLAG_FULLARGV
1325 /* Description: Wraps a stream socket with SSL/TLS and returns a new channel */
1327 { "verify",
1328 NULL,
1329 aio_cmd_verify,
1332 /* Description: Verifies the certificate of a SSL/TLS channel */
1334 #endif
1335 { NULL }
1338 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1340 return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, aio_command_table, argc, argv), argc, argv);
1343 static int JimAioOpenCommand(Jim_Interp *interp, int argc,
1344 Jim_Obj *const *argv)
1346 const char *mode;
1348 if (argc != 2 && argc != 3) {
1349 Jim_WrongNumArgs(interp, 1, argv, "filename ?mode?");
1350 return JIM_ERR;
1353 mode = (argc == 3) ? Jim_String(argv[2]) : "r";
1355 #ifdef jim_ext_tclcompat
1357 const char *filename = Jim_String(argv[1]);
1359 /* If the filename starts with '|', use popen instead */
1360 if (*filename == '|') {
1361 Jim_Obj *evalObj[3];
1363 evalObj[0] = Jim_NewStringObj(interp, "::popen", -1);
1364 evalObj[1] = Jim_NewStringObj(interp, filename + 1, -1);
1365 evalObj[2] = Jim_NewStringObj(interp, mode, -1);
1367 return Jim_EvalObjVector(interp, 3, evalObj);
1370 #endif
1371 return JimMakeChannel(interp, NULL, -1, argv[1], "aio.handle%ld", 0, mode) ? JIM_OK : JIM_ERR;
1374 #if defined(JIM_SSL)
1375 static void JimAioSslContextDelProc(struct Jim_Interp *interp, void *privData)
1377 SSL_CTX_free((SSL_CTX *)privData);
1378 ERR_free_strings();
1381 static SSL_CTX *JimAioSslCtx(Jim_Interp *interp)
1383 SSL_CTX *ssl_ctx = (SSL_CTX *)Jim_GetAssocData(interp, "ssl_ctx");
1384 if (ssl_ctx == NULL) {
1385 SSL_load_error_strings();
1386 SSL_library_init();
1387 ssl_ctx = SSL_CTX_new(TLSv1_2_method());
1388 if (ssl_ctx && SSL_CTX_set_default_verify_paths(ssl_ctx)) {
1389 SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
1390 Jim_SetAssocData(interp, "ssl_ctx", JimAioSslContextDelProc, ssl_ctx);
1391 } else {
1392 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1395 return ssl_ctx;
1397 #endif
1400 * Creates a channel for fh/fd/filename.
1402 * If fh is not NULL, uses that as the channel (and sets AIO_KEEPOPEN).
1403 * Otherwise, if fd is >= 0, uses that as the channel.
1404 * Otherwise opens 'filename' with mode 'mode'.
1406 * hdlfmt is a sprintf format for the filehandle. Anything with %ld at the end will do.
1407 * mode is used for open or fdopen.
1409 * Creates the command and sets the name as the current result.
1410 * Returns the AioFile pointer on sucess or NULL on failure.
1412 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
1413 const char *hdlfmt, int family, const char *mode)
1415 AioFile *af;
1416 char buf[AIO_CMD_LEN];
1417 int openFlags = 0;
1419 snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
1421 if (fh) {
1422 openFlags = AIO_KEEPOPEN;
1425 snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
1426 if (!filename) {
1427 filename = Jim_NewStringObj(interp, buf, -1);
1430 Jim_IncrRefCount(filename);
1432 if (fh == NULL) {
1433 #if !defined(JIM_ANSIC)
1434 if (fd >= 0) {
1435 fh = fdopen(fd, mode);
1437 else
1438 #endif
1439 fh = fopen(Jim_String(filename), mode);
1441 if (fh == NULL) {
1442 JimAioSetError(interp, filename);
1443 #if !defined(JIM_ANSIC)
1444 if (fd >= 0) {
1445 close(fd);
1447 #endif
1448 Jim_DecrRefCount(interp, filename);
1449 return NULL;
1453 /* Create the file command */
1454 af = Jim_Alloc(sizeof(*af));
1455 memset(af, 0, sizeof(*af));
1456 af->fp = fh;
1457 af->fd = fileno(fh);
1458 af->filename = filename;
1459 #ifdef FD_CLOEXEC
1460 if ((openFlags & AIO_KEEPOPEN) == 0) {
1461 (void)fcntl(af->fd, F_SETFD, FD_CLOEXEC);
1463 #endif
1464 af->openFlags = openFlags;
1465 af->addr_family = family;
1466 af->fops = &stdio_fops;
1467 af->ssl = NULL;
1469 Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc);
1471 /* Note that the command must use the global namespace, even if
1472 * the current namespace is something different
1474 Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, Jim_NewStringObj(interp, buf, -1)));
1476 return af;
1479 #if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && defined(HAVE_SYS_UN_H))
1481 * Create a pair of channels. e.g. from pipe() or socketpair()
1483 static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename,
1484 const char *hdlfmt, int family, const char *mode[2])
1486 if (JimMakeChannel(interp, NULL, p[0], filename, hdlfmt, family, mode[0])) {
1487 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
1488 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
1490 if (JimMakeChannel(interp, NULL, p[1], filename, hdlfmt, family, mode[1])) {
1491 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
1492 Jim_SetResult(interp, objPtr);
1493 return JIM_OK;
1497 /* Can only be here if fdopen() failed */
1498 close(p[0]);
1499 close(p[1]);
1500 JimAioSetError(interp, NULL);
1501 return JIM_ERR;
1503 #endif
1505 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
1507 static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1509 const char *hdlfmt = "aio.unknown%ld";
1510 const char *socktypes[] = {
1511 "unix",
1512 "unix.server",
1513 "dgram",
1514 "dgram.server",
1515 "stream",
1516 "stream.server",
1517 "pipe",
1518 "pair",
1519 NULL
1521 enum
1523 SOCK_UNIX,
1524 SOCK_UNIX_SERVER,
1525 SOCK_DGRAM_CLIENT,
1526 SOCK_DGRAM_SERVER,
1527 SOCK_STREAM_CLIENT,
1528 SOCK_STREAM_SERVER,
1529 SOCK_STREAM_PIPE,
1530 SOCK_STREAM_SOCKETPAIR,
1532 int socktype;
1533 int sock;
1534 const char *hostportarg = NULL;
1535 int res;
1536 int on = 1;
1537 const char *mode = "r+";
1538 int family = PF_INET;
1539 Jim_Obj *argv0 = argv[0];
1540 int ipv6 = 0;
1542 if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-ipv6")) {
1543 if (!IPV6) {
1544 Jim_SetResultString(interp, "ipv6 not supported", -1);
1545 return JIM_ERR;
1547 ipv6 = 1;
1548 family = PF_INET6;
1550 argc -= ipv6;
1551 argv += ipv6;
1553 if (argc < 2) {
1554 wrongargs:
1555 Jim_WrongNumArgs(interp, 1, &argv0, "?-ipv6? type ?address?");
1556 return JIM_ERR;
1559 if (Jim_GetEnum(interp, argv[1], socktypes, &socktype, "socket type", JIM_ERRMSG) != JIM_OK)
1560 return JIM_ERR;
1562 Jim_SetEmptyResult(interp);
1564 hdlfmt = "aio.sock%ld";
1566 if (argc > 2) {
1567 hostportarg = Jim_String(argv[2]);
1570 switch (socktype) {
1571 case SOCK_DGRAM_CLIENT:
1572 if (argc == 2) {
1573 /* No address, so an unconnected dgram socket */
1574 sock = socket(family, SOCK_DGRAM, 0);
1575 if (sock < 0) {
1576 JimAioSetError(interp, NULL);
1577 return JIM_ERR;
1579 break;
1581 /* fall through */
1582 case SOCK_STREAM_CLIENT:
1584 union sockaddr_any sa;
1585 int salen;
1587 if (argc != 3) {
1588 goto wrongargs;
1591 if (ipv6) {
1592 if (JimParseIPv6Address(interp, hostportarg, &sa, &salen) != JIM_OK) {
1593 return JIM_ERR;
1596 else if (JimParseIpAddress(interp, hostportarg, &sa, &salen) != JIM_OK) {
1597 return JIM_ERR;
1599 sock = socket(family, (socktype == SOCK_DGRAM_CLIENT) ? SOCK_DGRAM : SOCK_STREAM, 0);
1600 if (sock < 0) {
1601 JimAioSetError(interp, NULL);
1602 return JIM_ERR;
1604 res = connect(sock, &sa.sa, salen);
1605 if (res) {
1606 JimAioSetError(interp, argv[2]);
1607 close(sock);
1608 return JIM_ERR;
1611 break;
1613 case SOCK_STREAM_SERVER:
1614 case SOCK_DGRAM_SERVER:
1616 union sockaddr_any sa;
1617 int salen;
1619 if (argc != 3) {
1620 goto wrongargs;
1623 if (ipv6) {
1624 if (JimParseIPv6Address(interp, hostportarg, &sa, &salen) != JIM_OK) {
1625 return JIM_ERR;
1628 else if (JimParseIpAddress(interp, hostportarg, &sa, &salen) != JIM_OK) {
1629 return JIM_ERR;
1631 sock = socket(family, (socktype == SOCK_DGRAM_SERVER) ? SOCK_DGRAM : SOCK_STREAM, 0);
1632 if (sock < 0) {
1633 JimAioSetError(interp, NULL);
1634 return JIM_ERR;
1637 /* Enable address reuse */
1638 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
1640 res = bind(sock, &sa.sa, salen);
1641 if (res) {
1642 JimAioSetError(interp, argv[2]);
1643 close(sock);
1644 return JIM_ERR;
1646 if (socktype == SOCK_STREAM_SERVER) {
1647 res = listen(sock, 5);
1648 if (res) {
1649 JimAioSetError(interp, NULL);
1650 close(sock);
1651 return JIM_ERR;
1654 hdlfmt = "aio.socksrv%ld";
1656 break;
1658 #ifdef HAVE_SYS_UN_H
1659 case SOCK_UNIX:
1661 struct sockaddr_un sa;
1662 socklen_t len;
1664 if (argc != 3 || ipv6) {
1665 goto wrongargs;
1668 if (JimParseDomainAddress(interp, hostportarg, &sa) != JIM_OK) {
1669 JimAioSetError(interp, argv[2]);
1670 return JIM_ERR;
1672 family = PF_UNIX;
1673 sock = socket(PF_UNIX, SOCK_STREAM, 0);
1674 if (sock < 0) {
1675 JimAioSetError(interp, NULL);
1676 return JIM_ERR;
1678 len = strlen(sa.sun_path) + 1 + sizeof(sa.sun_family);
1679 res = connect(sock, (struct sockaddr *)&sa, len);
1680 if (res) {
1681 JimAioSetError(interp, argv[2]);
1682 close(sock);
1683 return JIM_ERR;
1685 hdlfmt = "aio.sockunix%ld";
1686 break;
1689 case SOCK_UNIX_SERVER:
1691 struct sockaddr_un sa;
1692 socklen_t len;
1694 if (argc != 3 || ipv6) {
1695 goto wrongargs;
1698 if (JimParseDomainAddress(interp, hostportarg, &sa) != JIM_OK) {
1699 JimAioSetError(interp, argv[2]);
1700 return JIM_ERR;
1702 family = PF_UNIX;
1703 sock = socket(PF_UNIX, SOCK_STREAM, 0);
1704 if (sock < 0) {
1705 JimAioSetError(interp, NULL);
1706 return JIM_ERR;
1708 len = strlen(sa.sun_path) + 1 + sizeof(sa.sun_family);
1709 res = bind(sock, (struct sockaddr *)&sa, len);
1710 if (res) {
1711 JimAioSetError(interp, argv[2]);
1712 close(sock);
1713 return JIM_ERR;
1715 res = listen(sock, 5);
1716 if (res) {
1717 JimAioSetError(interp, NULL);
1718 close(sock);
1719 return JIM_ERR;
1721 hdlfmt = "aio.sockunixsrv%ld";
1722 break;
1724 #endif
1726 #if defined(HAVE_SOCKETPAIR) && defined(HAVE_SYS_UN_H)
1727 case SOCK_STREAM_SOCKETPAIR:
1729 int p[2];
1730 static const char *mode[2] = { "r+", "r+" };
1732 if (argc != 2 || ipv6) {
1733 goto wrongargs;
1736 if (socketpair(PF_UNIX, SOCK_STREAM, 0, p) < 0) {
1737 JimAioSetError(interp, NULL);
1738 return JIM_ERR;
1740 return JimMakeChannelPair(interp, p, argv[1], "aio.sockpair%ld", PF_UNIX, mode);
1742 break;
1743 #endif
1745 #if defined(HAVE_PIPE)
1746 case SOCK_STREAM_PIPE:
1748 int p[2];
1749 static const char *mode[2] = { "r", "w" };
1751 if (argc != 2 || ipv6) {
1752 goto wrongargs;
1755 if (pipe(p) < 0) {
1756 JimAioSetError(interp, NULL);
1757 return JIM_ERR;
1760 return JimMakeChannelPair(interp, p, argv[1], "aio.pipe%ld", 0, mode);
1762 break;
1763 #endif
1765 default:
1766 Jim_SetResultString(interp, "Unsupported socket type", -1);
1767 return JIM_ERR;
1770 return JimMakeChannel(interp, NULL, sock, argv[1], hdlfmt, family, mode) ? JIM_OK : JIM_ERR;
1772 #endif /* JIM_BOOTSTRAP */
1775 * Returns the file descriptor of a writable, newly created temp file
1776 * or -1 on error.
1778 * On success, leaves the filename in the interpreter result, otherwise
1779 * leaves an error message.
1781 int Jim_MakeTempFile(Jim_Interp *interp, const char *template)
1783 #ifdef HAVE_MKSTEMP
1784 int fd;
1785 mode_t mask;
1786 Jim_Obj *filenameObj;
1788 if (template == NULL) {
1789 const char *tmpdir = getenv("TMPDIR");
1790 if (tmpdir == NULL || *tmpdir == '\0' || access(tmpdir, W_OK) != 0) {
1791 tmpdir = "/tmp/";
1793 filenameObj = Jim_NewStringObj(interp, tmpdir, -1);
1794 if (tmpdir[0] && tmpdir[strlen(tmpdir) - 1] != '/') {
1795 Jim_AppendString(interp, filenameObj, "/", 1);
1797 Jim_AppendString(interp, filenameObj, "tcl.tmp.XXXXXX", -1);
1799 else {
1800 filenameObj = Jim_NewStringObj(interp, template, -1);
1803 mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
1805 /* Update the template name directly with the filename */
1806 fd = mkstemp(filenameObj->bytes);
1807 umask(mask);
1808 if (fd < 0) {
1809 JimAioSetError(interp, filenameObj);
1810 Jim_FreeNewObj(interp, filenameObj);
1811 return -1;
1814 Jim_SetResult(interp, filenameObj);
1815 return fd;
1816 #else
1817 Jim_SetResultString(interp, "platform has no tempfile support", -1);
1818 return -1;
1819 #endif
1822 #if defined(JIM_SSL)
1823 static int JimAioLoadSSLCertsCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1825 SSL_CTX *ssl_ctx;
1827 if (argc != 2) {
1828 Jim_WrongNumArgs(interp, 1, argv, "dir");
1829 return JIM_ERR;
1832 ssl_ctx = JimAioSslCtx(interp);
1833 if (!ssl_ctx) {
1834 return JIM_ERR;
1836 if (SSL_CTX_load_verify_locations(ssl_ctx, NULL, Jim_String(argv[1])) == 1) {
1837 return JIM_OK;
1839 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1840 return JIM_ERR;
1842 #endif
1844 int Jim_aioInit(Jim_Interp *interp)
1846 if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG))
1847 return JIM_ERR;
1849 #if defined(JIM_SSL)
1850 Jim_CreateCommand(interp, "load_ssl_certs", JimAioLoadSSLCertsCommand, NULL, NULL);
1851 #endif
1853 Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL);
1854 #ifndef JIM_ANSIC
1855 Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL);
1856 #endif
1858 /* Create filehandles for stdin, stdout and stderr */
1859 JimMakeChannel(interp, stdin, -1, NULL, "stdin", 0, "r");
1860 JimMakeChannel(interp, stdout, -1, NULL, "stdout", 0, "w");
1861 JimMakeChannel(interp, stderr, -1, NULL, "stderr", 0, "w");
1863 return JIM_OK;