auto.def: tclprefix should not be enabled by default
[jimtcl.git] / jim-aio.c
blob67afac157d8a2254bba3cbafe115b8fa06611ea5
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 <netinet/tcp.h>
60 #include <arpa/inet.h>
61 #include <netdb.h>
62 #ifdef HAVE_SYS_UN_H
63 #include <sys/un.h>
64 #endif
65 #else
66 #define JIM_ANSIC
67 #endif
69 #if defined(JIM_SSL)
70 #include <openssl/ssl.h>
71 #include <openssl/err.h>
72 #endif
74 #ifdef HAVE_TERMIOS_H
75 #include <jim-tty.h>
76 #endif
78 #include "jim-eventloop.h"
79 #include "jim-subcmd.h"
81 #define AIO_CMD_LEN 32 /* e.g. aio.handleXXXXXX */
82 #define AIO_BUF_LEN 256 /* Can keep this small and rely on stdio buffering */
84 #ifndef HAVE_FTELLO
85 #define ftello ftell
86 #endif
87 #ifndef HAVE_FSEEKO
88 #define fseeko fseek
89 #endif
91 #define AIO_KEEPOPEN 1
93 #if defined(JIM_IPV6)
94 #define IPV6 1
95 #else
96 #define IPV6 0
97 #ifndef PF_INET6
98 #define PF_INET6 0
99 #endif
100 #endif
102 #define JimCheckStreamError(interp, af) af->fops->error(af)
104 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
105 union sockaddr_any {
106 struct sockaddr sa;
107 struct sockaddr_in sin;
108 #if IPV6
109 struct sockaddr_in6 sin6;
110 #endif
113 #ifndef HAVE_INET_NTOP
114 const char *inet_ntop(int af, const void *src, char *dst, int size)
116 if (af != PF_INET) {
117 return NULL;
119 snprintf(dst, size, "%s", inet_ntoa(((struct sockaddr_in *)src)->sin_addr));
120 return dst;
122 #endif
123 #endif /* JIM_BOOTSTRAP */
125 struct AioFile;
127 typedef struct {
128 int (*writer)(struct AioFile *af, const char *buf, int len);
129 int (*reader)(struct AioFile *af, char *buf, int len);
130 const char *(*getline)(struct AioFile *af, char *buf, int len);
131 int (*error)(const struct AioFile *af);
132 const char *(*strerror)(struct AioFile *af);
133 int (*verify)(struct AioFile *af);
134 } JimAioFopsType;
136 typedef struct AioFile
138 FILE *fp;
139 Jim_Obj *filename;
140 int type;
141 int openFlags; /* AIO_KEEPOPEN? keep FILE* */
142 int fd;
143 Jim_Obj *rEvent;
144 Jim_Obj *wEvent;
145 Jim_Obj *eEvent;
146 int addr_family;
147 void *ssl;
148 const JimAioFopsType *fops;
149 } AioFile;
151 static int stdio_writer(struct AioFile *af, const char *buf, int len)
153 return fwrite(buf, 1, len, af->fp);
156 static int stdio_reader(struct AioFile *af, char *buf, int len)
158 return fread(buf, 1, len, af->fp);
161 static const char *stdio_getline(struct AioFile *af, char *buf, int len)
163 return fgets(buf, len, af->fp);
166 static int stdio_error(const AioFile *af)
168 if (!ferror(af->fp)) {
169 return JIM_OK;
171 clearerr(af->fp);
172 /* EAGAIN and similar are not error conditions. Just treat them like eof */
173 if (feof(af->fp) || errno == EAGAIN || errno == EINTR) {
174 return JIM_OK;
176 #ifdef ECONNRESET
177 if (errno == ECONNRESET) {
178 return JIM_OK;
180 #endif
181 #ifdef ECONNABORTED
182 if (errno == ECONNABORTED) {
183 return JIM_OK;
185 #endif
186 return JIM_ERR;
189 static const char *stdio_strerror(struct AioFile *af)
191 return strerror(errno);
194 static const JimAioFopsType stdio_fops = {
195 stdio_writer,
196 stdio_reader,
197 stdio_getline,
198 stdio_error,
199 stdio_strerror,
200 NULL
203 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
205 static SSL_CTX *JimAioSslCtx(Jim_Interp *interp);
207 static int ssl_writer(struct AioFile *af, const char *buf, int len)
209 return SSL_write(af->ssl, buf, len);
212 static int ssl_reader(struct AioFile *af, char *buf, int len)
214 return SSL_read(af->ssl, buf, len);
217 static const char *ssl_getline(struct AioFile *af, char *buf, int len)
219 int i;
220 for (i = 0; i < len + 1; i++) {
221 if (SSL_read(af->ssl, &buf[i], 1) != 1) {
222 if (i == 0) {
223 return NULL;
225 break;
227 if (buf[i] == '\n') {
228 break;
231 buf[i] = '\0';
232 return buf;
235 static int ssl_error(const struct AioFile *af)
237 if (ERR_peek_error() == 0) {
238 return JIM_OK;
241 return JIM_ERR;
244 static const char *ssl_strerror(struct AioFile *af)
246 int err = ERR_get_error();
248 if (err) {
249 return ERR_error_string(err, NULL);
252 /* should not happen */
253 return "unknown SSL error";
256 static int ssl_verify(struct AioFile *af)
258 X509 *cert;
260 cert = SSL_get_peer_certificate(af->ssl);
261 if (!cert) {
262 return JIM_ERR;
264 X509_free(cert);
266 if (SSL_get_verify_result(af->ssl) == X509_V_OK) {
267 return JIM_OK;
270 return JIM_ERR;
273 static const JimAioFopsType ssl_fops = {
274 ssl_writer,
275 ssl_reader,
276 ssl_getline,
277 ssl_error,
278 ssl_strerror,
279 ssl_verify
281 #endif /* JIM_BOOTSTRAP */
283 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
284 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
285 const char *hdlfmt, int family, const char *mode);
287 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
288 static int JimParseIPv6Address(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, int *salen)
290 #if IPV6
292 * An IPv6 addr/port looks like:
293 * [::1]
294 * [::1]:2000
295 * [fe80::223:6cff:fe95:bdc0%en1]:2000
296 * [::]:2000
297 * 2000
299 * Note that the "any" address is ::, which is the same as when no address is specified.
301 char *sthost = NULL;
302 const char *stport;
303 int ret = JIM_OK;
304 struct addrinfo req;
305 struct addrinfo *ai;
307 stport = strrchr(hostport, ':');
308 if (!stport) {
309 /* No : so, the whole thing is the port */
310 stport = hostport;
311 hostport = "::";
312 sthost = Jim_StrDup(hostport);
314 else {
315 stport++;
318 if (*hostport == '[') {
319 /* This is a numeric ipv6 address */
320 char *pt = strchr(++hostport, ']');
321 if (pt) {
322 sthost = Jim_StrDupLen(hostport, pt - hostport);
326 if (!sthost) {
327 sthost = Jim_StrDupLen(hostport, stport - hostport - 1);
330 memset(&req, '\0', sizeof(req));
331 req.ai_family = PF_INET6;
333 if (getaddrinfo(sthost, NULL, &req, &ai)) {
334 Jim_SetResultFormatted(interp, "Not a valid address: %s", hostport);
335 ret = JIM_ERR;
337 else {
338 memcpy(&sa->sin, ai->ai_addr, ai->ai_addrlen);
339 *salen = ai->ai_addrlen;
341 sa->sin.sin_port = htons(atoi(stport));
343 freeaddrinfo(ai);
345 Jim_Free(sthost);
347 return ret;
348 #else
349 Jim_SetResultString(interp, "ipv6 not supported", -1);
350 return JIM_ERR;
351 #endif
354 static int JimParseIpAddress(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, int *salen)
356 /* An IPv4 addr/port looks like:
357 * 192.168.1.5
358 * 192.168.1.5:2000
359 * 2000
361 * If the address is missing, INADDR_ANY is used.
362 * If the port is missing, 0 is used (only useful for server sockets).
364 char *sthost = NULL;
365 const char *stport;
366 int ret = JIM_OK;
368 stport = strrchr(hostport, ':');
369 if (!stport) {
370 /* No : so, the whole thing is the port */
371 stport = hostport;
372 sthost = Jim_StrDup("0.0.0.0");
374 else {
375 sthost = Jim_StrDupLen(hostport, stport - hostport);
376 stport++;
380 #ifdef HAVE_GETADDRINFO
381 struct addrinfo req;
382 struct addrinfo *ai;
383 memset(&req, '\0', sizeof(req));
384 req.ai_family = PF_INET;
386 if (getaddrinfo(sthost, NULL, &req, &ai)) {
387 ret = JIM_ERR;
389 else {
390 memcpy(&sa->sin, ai->ai_addr, ai->ai_addrlen);
391 *salen = ai->ai_addrlen;
392 freeaddrinfo(ai);
394 #else
395 struct hostent *he;
397 ret = JIM_ERR;
399 if ((he = gethostbyname(sthost)) != NULL) {
400 if (he->h_length == sizeof(sa->sin.sin_addr)) {
401 *salen = sizeof(sa->sin);
402 sa->sin.sin_family= he->h_addrtype;
403 memcpy(&sa->sin.sin_addr, he->h_addr, he->h_length); /* set address */
404 ret = JIM_OK;
407 #endif
409 sa->sin.sin_port = htons(atoi(stport));
411 Jim_Free(sthost);
413 if (ret != JIM_OK) {
414 Jim_SetResultFormatted(interp, "Not a valid address: %s", hostport);
417 return ret;
420 #ifdef HAVE_SYS_UN_H
421 static int JimParseDomainAddress(Jim_Interp *interp, const char *path, struct sockaddr_un *sa)
423 sa->sun_family = PF_UNIX;
424 snprintf(sa->sun_path, sizeof(sa->sun_path), "%s", path);
426 return JIM_OK;
428 #endif
431 * Format that address in 'sa' as a string and store in variable 'varObjPtr'
433 static int JimFormatIpAddress(Jim_Interp *interp, Jim_Obj *varObjPtr, const union sockaddr_any *sa)
435 /* INET6_ADDRSTRLEN is 46. Add some for [] and port */
436 char addrbuf[60];
438 #if IPV6
439 if (sa->sa.sa_family == PF_INET6) {
440 addrbuf[0] = '[';
441 /* Allow 9 for []:65535\0 */
442 inet_ntop(sa->sa.sa_family, &sa->sin6.sin6_addr, addrbuf + 1, sizeof(addrbuf) - 9);
443 snprintf(addrbuf + strlen(addrbuf), 8, "]:%d", ntohs(sa->sin.sin_port));
445 else
446 #endif
447 if (sa->sa.sa_family == PF_INET) {
448 /* Allow 7 for :65535\0 */
449 inet_ntop(sa->sa.sa_family, &sa->sin.sin_addr, addrbuf, sizeof(addrbuf) - 7);
450 snprintf(addrbuf + strlen(addrbuf), 7, ":%d", ntohs(sa->sin.sin_port));
452 else {
453 /* recvfrom still works on unix domain sockets, etc */
454 addrbuf[0] = 0;
457 return Jim_SetVariable(interp, varObjPtr, Jim_NewStringObj(interp, addrbuf, -1));
460 #endif /* JIM_BOOTSTRAP */
462 static const char *JimAioErrorString(AioFile *af)
464 if (af && af->fops)
465 return af->fops->strerror(af);
467 return strerror(errno);
470 static void JimAioSetError(Jim_Interp *interp, Jim_Obj *name)
472 AioFile *af = Jim_CmdPrivData(interp);
474 if (name) {
475 Jim_SetResultFormatted(interp, "%#s: %s", name, JimAioErrorString(af));
477 else {
478 Jim_SetResultString(interp, JimAioErrorString(af), -1);
482 static void JimAioDelProc(Jim_Interp *interp, void *privData)
484 AioFile *af = privData;
486 JIM_NOTUSED(interp);
488 Jim_DecrRefCount(interp, af->filename);
490 #ifdef jim_ext_eventloop
491 /* remove all existing EventHandlers */
492 Jim_DeleteFileHandler(interp, af->fd, JIM_EVENT_READABLE | JIM_EVENT_WRITABLE | JIM_EVENT_EXCEPTION);
493 #endif
495 #if defined(JIM_SSL)
496 if (af->ssl != NULL) {
497 SSL_free(af->ssl);
499 #endif
500 if (!(af->openFlags & AIO_KEEPOPEN)) {
501 fclose(af->fp);
504 Jim_Free(af);
507 static int aio_cmd_read(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
509 AioFile *af = Jim_CmdPrivData(interp);
510 char buf[AIO_BUF_LEN];
511 Jim_Obj *objPtr;
512 int nonewline = 0;
513 jim_wide neededLen = -1; /* -1 is "read as much as possible" */
515 if (argc && Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
516 nonewline = 1;
517 argv++;
518 argc--;
520 if (argc == 1) {
521 if (Jim_GetWide(interp, argv[0], &neededLen) != JIM_OK)
522 return JIM_ERR;
523 if (neededLen < 0) {
524 Jim_SetResultString(interp, "invalid parameter: negative len", -1);
525 return JIM_ERR;
528 else if (argc) {
529 return -1;
531 objPtr = Jim_NewStringObj(interp, NULL, 0);
532 while (neededLen != 0) {
533 int retval;
534 int readlen;
536 if (neededLen == -1) {
537 readlen = AIO_BUF_LEN;
539 else {
540 readlen = (neededLen > AIO_BUF_LEN ? AIO_BUF_LEN : neededLen);
542 retval = af->fops->reader(af, buf, readlen);
543 if (retval > 0) {
544 Jim_AppendString(interp, objPtr, buf, retval);
545 if (neededLen != -1) {
546 neededLen -= retval;
549 if (retval != readlen)
550 break;
552 /* Check for error conditions */
553 if (JimCheckStreamError(interp, af)) {
554 Jim_FreeNewObj(interp, objPtr);
555 return JIM_ERR;
557 if (nonewline) {
558 int len;
559 const char *s = Jim_GetString(objPtr, &len);
561 if (len > 0 && s[len - 1] == '\n') {
562 objPtr->length--;
563 objPtr->bytes[objPtr->length] = '\0';
566 Jim_SetResult(interp, objPtr);
567 return JIM_OK;
570 AioFile *Jim_AioFile(Jim_Interp *interp, Jim_Obj *command)
572 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, command, JIM_ERRMSG);
574 /* XXX: There ought to be a supported API for this */
575 if (cmdPtr && !cmdPtr->isproc && cmdPtr->u.native.cmdProc == JimAioSubCmdProc) {
576 return (AioFile *) cmdPtr->u.native.privData;
578 Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", command);
579 return NULL;
582 FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command)
584 AioFile *af;
586 af = Jim_AioFile(interp, command);
587 if (af == NULL) {
588 return NULL;
591 return af->fp;
594 static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
596 AioFile *af = Jim_CmdPrivData(interp);
597 jim_wide count = 0;
598 jim_wide maxlen = JIM_WIDE_MAX;
599 AioFile *outf = Jim_AioFile(interp, argv[0]);
601 if (outf == NULL) {
602 return JIM_ERR;
605 if (argc == 2) {
606 if (Jim_GetWide(interp, argv[1], &maxlen) != JIM_OK) {
607 return JIM_ERR;
611 while (count < maxlen) {
612 char ch;
614 if (af->fops->reader(af, &ch, 1) != 1) {
615 break;
617 if (outf->fops->writer(outf, &ch, 1) != 1) {
618 break;
620 count++;
623 if (JimCheckStreamError(interp, af) || JimCheckStreamError(interp, outf)) {
624 return JIM_ERR;
627 Jim_SetResultInt(interp, count);
629 return JIM_OK;
632 static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
634 AioFile *af = Jim_CmdPrivData(interp);
635 char buf[AIO_BUF_LEN];
636 Jim_Obj *objPtr;
637 int len;
639 errno = 0;
641 objPtr = Jim_NewStringObj(interp, NULL, 0);
642 while (1) {
643 buf[AIO_BUF_LEN - 1] = '_';
645 if (af->fops->getline(af, buf, AIO_BUF_LEN) == NULL)
646 break;
648 if (buf[AIO_BUF_LEN - 1] == '\0' && buf[AIO_BUF_LEN - 2] != '\n') {
649 Jim_AppendString(interp, objPtr, buf, AIO_BUF_LEN - 1);
651 else {
652 len = strlen(buf);
654 if (len && (buf[len - 1] == '\n')) {
655 /* strip "\n" */
656 len--;
659 Jim_AppendString(interp, objPtr, buf, len);
660 break;
664 if (JimCheckStreamError(interp, af)) {
665 /* I/O error */
666 Jim_FreeNewObj(interp, objPtr);
667 return JIM_ERR;
670 if (argc) {
671 if (Jim_SetVariable(interp, argv[0], objPtr) != JIM_OK) {
672 Jim_FreeNewObj(interp, objPtr);
673 return JIM_ERR;
676 len = Jim_Length(objPtr);
678 if (len == 0 && feof(af->fp)) {
679 /* On EOF returns -1 if varName was specified */
680 len = -1;
682 Jim_SetResultInt(interp, len);
684 else {
685 Jim_SetResult(interp, objPtr);
687 return JIM_OK;
690 static int aio_cmd_puts(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
692 AioFile *af = Jim_CmdPrivData(interp);
693 int wlen;
694 const char *wdata;
695 Jim_Obj *strObj;
697 if (argc == 2) {
698 if (!Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
699 return -1;
701 strObj = argv[1];
703 else {
704 strObj = argv[0];
707 wdata = Jim_GetString(strObj, &wlen);
708 if (af->fops->writer(af, wdata, wlen) == wlen) {
709 if (argc == 2 || af->fops->writer(af, "\n", 1) == 1) {
710 return JIM_OK;
713 JimAioSetError(interp, af->filename);
714 return JIM_ERR;
717 static int aio_cmd_isatty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
719 #ifdef HAVE_ISATTY
720 AioFile *af = Jim_CmdPrivData(interp);
721 Jim_SetResultInt(interp, isatty(fileno(af->fp)));
722 #else
723 Jim_SetResultInt(interp, 0);
724 #endif
726 return JIM_OK;
729 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
730 static int aio_cmd_recvfrom(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
732 AioFile *af = Jim_CmdPrivData(interp);
733 char *buf;
734 union sockaddr_any sa;
735 long len;
736 socklen_t salen = sizeof(sa);
737 int rlen;
739 if (Jim_GetLong(interp, argv[0], &len) != JIM_OK) {
740 return JIM_ERR;
743 buf = Jim_Alloc(len + 1);
745 rlen = recvfrom(fileno(af->fp), buf, len, 0, &sa.sa, &salen);
746 if (rlen < 0) {
747 Jim_Free(buf);
748 JimAioSetError(interp, NULL);
749 return JIM_ERR;
751 buf[rlen] = 0;
752 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, rlen));
754 if (argc > 1) {
755 return JimFormatIpAddress(interp, argv[1], &sa);
758 return JIM_OK;
762 static int aio_cmd_sendto(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
764 AioFile *af = Jim_CmdPrivData(interp);
765 int wlen;
766 int len;
767 const char *wdata;
768 union sockaddr_any sa;
769 const char *addr = Jim_String(argv[1]);
770 int salen;
772 if (IPV6 && af->addr_family == PF_INET6) {
773 if (JimParseIPv6Address(interp, addr, &sa, &salen) != JIM_OK) {
774 return JIM_ERR;
777 else if (JimParseIpAddress(interp, addr, &sa, &salen) != JIM_OK) {
778 return JIM_ERR;
780 wdata = Jim_GetString(argv[0], &wlen);
782 /* Note that we don't validate the socket type. Rely on sendto() failing if appropriate */
783 len = sendto(fileno(af->fp), wdata, wlen, 0, &sa.sa, salen);
784 if (len < 0) {
785 JimAioSetError(interp, NULL);
786 return JIM_ERR;
788 Jim_SetResultInt(interp, len);
789 return JIM_OK;
792 static int aio_cmd_accept(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
794 AioFile *af = Jim_CmdPrivData(interp);
795 int sock;
796 union sockaddr_any sa;
797 socklen_t addrlen = sizeof(sa);
799 sock = accept(af->fd, &sa.sa, &addrlen);
800 if (sock < 0) {
801 JimAioSetError(interp, NULL);
802 return JIM_ERR;
805 if (argc > 0) {
806 if (JimFormatIpAddress(interp, argv[0], &sa) != JIM_OK) {
807 return JIM_ERR;
811 /* Create the file command */
812 return JimMakeChannel(interp, NULL, sock, Jim_NewStringObj(interp, "accept", -1),
813 "aio.sockstream%ld", af->addr_family, "r+") ? JIM_OK : JIM_ERR;
816 static int aio_cmd_listen(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
818 AioFile *af = Jim_CmdPrivData(interp);
819 long backlog;
821 if (Jim_GetLong(interp, argv[0], &backlog) != JIM_OK) {
822 return JIM_ERR;
825 if (listen(af->fd, backlog)) {
826 JimAioSetError(interp, NULL);
827 return JIM_ERR;
830 return JIM_OK;
832 #endif /* JIM_BOOTSTRAP */
834 static int aio_cmd_flush(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
836 AioFile *af = Jim_CmdPrivData(interp);
838 if (fflush(af->fp) == EOF) {
839 JimAioSetError(interp, af->filename);
840 return JIM_ERR;
842 return JIM_OK;
845 static int aio_cmd_eof(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
847 AioFile *af = Jim_CmdPrivData(interp);
849 Jim_SetResultInt(interp, feof(af->fp));
850 return JIM_OK;
853 static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
855 if (argc == 3) {
856 #if !defined(JIM_ANSIC) && defined(HAVE_SHUTDOWN)
857 static const char * const options[] = { "r", "w", NULL };
858 enum { OPT_R, OPT_W, };
859 int option;
860 AioFile *af = Jim_CmdPrivData(interp);
862 if (Jim_GetEnum(interp, argv[2], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
863 return JIM_ERR;
865 if (shutdown(af->fd, option == OPT_R ? SHUT_RD : SHUT_WR) == 0) {
866 return JIM_OK;
868 JimAioSetError(interp, NULL);
869 #else
870 Jim_SetResultString(interp, "async close not supported", -1);
871 #endif
872 return JIM_ERR;
875 return Jim_DeleteCommand(interp, Jim_String(argv[0]));
878 static int aio_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
880 AioFile *af = Jim_CmdPrivData(interp);
881 int orig = SEEK_SET;
882 jim_wide offset;
884 if (argc == 2) {
885 if (Jim_CompareStringImmediate(interp, argv[1], "start"))
886 orig = SEEK_SET;
887 else if (Jim_CompareStringImmediate(interp, argv[1], "current"))
888 orig = SEEK_CUR;
889 else if (Jim_CompareStringImmediate(interp, argv[1], "end"))
890 orig = SEEK_END;
891 else {
892 return -1;
895 if (Jim_GetWide(interp, argv[0], &offset) != JIM_OK) {
896 return JIM_ERR;
898 if (fseeko(af->fp, offset, orig) == -1) {
899 JimAioSetError(interp, af->filename);
900 return JIM_ERR;
902 return JIM_OK;
905 static int aio_cmd_tell(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
907 AioFile *af = Jim_CmdPrivData(interp);
909 Jim_SetResultInt(interp, ftello(af->fp));
910 return JIM_OK;
913 static int aio_cmd_filename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
915 AioFile *af = Jim_CmdPrivData(interp);
917 Jim_SetResult(interp, af->filename);
918 return JIM_OK;
921 #ifdef O_NDELAY
922 static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
924 AioFile *af = Jim_CmdPrivData(interp);
926 int fmode = fcntl(af->fd, F_GETFL);
928 if (argc) {
929 long nb;
931 if (Jim_GetLong(interp, argv[0], &nb) != JIM_OK) {
932 return JIM_ERR;
934 if (nb) {
935 fmode |= O_NDELAY;
937 else {
938 fmode &= ~O_NDELAY;
940 (void)fcntl(af->fd, F_SETFL, fmode);
942 Jim_SetResultInt(interp, (fmode & O_NONBLOCK) ? 1 : 0);
943 return JIM_OK;
945 #endif
947 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
948 #define SOCKOPT_BOOL 0
949 #define SOCKOPT_INT 1
950 #define SOCKOPT_TIMEVAL 2 /* not currently supported */
952 static const struct sockopt_def {
953 const char *name;
954 int level;
955 int opt;
956 int type; /* SOCKOPT_xxx */
957 } sockopts[] = {
958 #ifdef SOL_SOCKET
959 #ifdef SO_BROADCAST
960 { "broadcast", SOL_SOCKET, SO_BROADCAST },
961 #endif
962 #ifdef SO_DEBUG
963 { "debug", SOL_SOCKET, SO_DEBUG },
964 #endif
965 #ifdef SO_KEEPALIVE
966 { "keepalive", SOL_SOCKET, SO_KEEPALIVE },
967 #endif
968 #ifdef SO_NOSIGPIPE
969 { "nosigpipe", SOL_SOCKET, SO_NOSIGPIPE },
970 #endif
971 #ifdef SO_OOBINLINE
972 { "oobinline", SOL_SOCKET, SO_OOBINLINE },
973 #endif
974 #ifdef SO_SNDBUF
975 { "sndbuf", SOL_SOCKET, SO_SNDBUF, SOCKOPT_INT },
976 #endif
977 #ifdef SO_RCVBUF
978 { "rcvbuf", SOL_SOCKET, SO_RCVBUF, SOCKOPT_INT },
979 #endif
980 #if 0 && defined(SO_SNDTIMEO)
981 { "sndtimeo", SOL_SOCKET, SO_SNDTIMEO, SOCKOPT_TIMEVAL },
982 #endif
983 #if 0 && defined(SO_RCVTIMEO)
984 { "rcvtimeo", SOL_SOCKET, SO_RCVTIMEO, SOCKOPT_TIMEVAL },
985 #endif
986 #endif
987 #ifdef IPPROTO_TCP
988 #ifdef TCP_NODELAY
989 { "tcp_nodelay", IPPROTO_TCP, TCP_NODELAY },
990 #endif
991 #endif
994 static int aio_cmd_sockopt(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
996 AioFile *af = Jim_CmdPrivData(interp);
997 int i;
999 if (argc == 0) {
1000 Jim_Obj *dictObjPtr = Jim_NewListObj(interp, NULL, 0);
1001 for (i = 0; i < sizeof(sockopts) / sizeof(*sockopts); i++) {
1002 int value = 0;
1003 socklen_t len = sizeof(value);
1004 if (getsockopt(af->fd, sockopts[i].level, sockopts[i].opt, (void *)&value, &len) == 0) {
1005 if (sockopts[i].type == SOCKOPT_BOOL) {
1006 value = !!value;
1008 Jim_ListAppendElement(interp, dictObjPtr, Jim_NewStringObj(interp, sockopts[i].name, -1));
1009 Jim_ListAppendElement(interp, dictObjPtr, Jim_NewIntObj(interp, value));
1012 Jim_SetResult(interp, dictObjPtr);
1013 return JIM_OK;
1015 if (argc == 1) {
1016 return -1;
1019 /* Set an option */
1020 for (i = 0; i < sizeof(sockopts) / sizeof(*sockopts); i++) {
1021 if (strcmp(Jim_String(argv[0]), sockopts[i].name) == 0) {
1022 int on;
1023 if (sockopts[i].type == SOCKOPT_BOOL) {
1024 if (Jim_GetBoolean(interp, argv[1], &on) != JIM_OK) {
1025 return JIM_ERR;
1028 else {
1029 long longval;
1030 if (Jim_GetLong(interp, argv[1], &longval) != JIM_OK) {
1031 return JIM_ERR;
1033 on = longval;
1035 if (setsockopt(af->fd, sockopts[i].level, sockopts[i].opt, (void *)&on, sizeof(on)) < 0) {
1036 Jim_SetResultFormatted(interp, "Failed to set %#s: %s", argv[0], strerror(errno));
1037 return JIM_ERR;
1039 return JIM_OK;
1042 /* Not found */
1043 Jim_SetResultFormatted(interp, "Unknown sockopt %#s", argv[0]);
1044 return JIM_ERR;
1046 #endif
1048 #ifdef HAVE_FSYNC
1049 static int aio_cmd_sync(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1051 AioFile *af = Jim_CmdPrivData(interp);
1053 fflush(af->fp);
1054 fsync(af->fd);
1055 return JIM_OK;
1057 #endif
1059 static int aio_cmd_buffering(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1061 AioFile *af = Jim_CmdPrivData(interp);
1063 static const char * const options[] = {
1064 "none",
1065 "line",
1066 "full",
1067 NULL
1069 enum
1071 OPT_NONE,
1072 OPT_LINE,
1073 OPT_FULL,
1075 int option;
1077 if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
1078 return JIM_ERR;
1080 switch (option) {
1081 case OPT_NONE:
1082 setvbuf(af->fp, NULL, _IONBF, 0);
1083 break;
1084 case OPT_LINE:
1085 setvbuf(af->fp, NULL, _IOLBF, BUFSIZ);
1086 break;
1087 case OPT_FULL:
1088 setvbuf(af->fp, NULL, _IOFBF, BUFSIZ);
1089 break;
1091 return JIM_OK;
1094 #ifdef jim_ext_eventloop
1095 static void JimAioFileEventFinalizer(Jim_Interp *interp, void *clientData)
1097 Jim_Obj **objPtrPtr = clientData;
1099 Jim_DecrRefCount(interp, *objPtrPtr);
1100 *objPtrPtr = NULL;
1103 static int JimAioFileEventHandler(Jim_Interp *interp, void *clientData, int mask)
1105 Jim_Obj **objPtrPtr = clientData;
1107 return Jim_EvalObjBackground(interp, *objPtrPtr);
1110 static int aio_eventinfo(Jim_Interp *interp, AioFile * af, unsigned mask, Jim_Obj **scriptHandlerObj,
1111 int argc, Jim_Obj * const *argv)
1113 if (argc == 0) {
1114 /* Return current script */
1115 if (*scriptHandlerObj) {
1116 Jim_SetResult(interp, *scriptHandlerObj);
1118 return JIM_OK;
1121 if (*scriptHandlerObj) {
1122 /* Delete old handler */
1123 Jim_DeleteFileHandler(interp, af->fd, mask);
1126 /* Now possibly add the new script(s) */
1127 if (Jim_Length(argv[0]) == 0) {
1128 /* Empty script, so done */
1129 return JIM_OK;
1132 /* A new script to add */
1133 Jim_IncrRefCount(argv[0]);
1134 *scriptHandlerObj = argv[0];
1136 Jim_CreateFileHandler(interp, af->fd, mask,
1137 JimAioFileEventHandler, scriptHandlerObj, JimAioFileEventFinalizer);
1139 return JIM_OK;
1142 static int aio_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1144 AioFile *af = Jim_CmdPrivData(interp);
1146 return aio_eventinfo(interp, af, JIM_EVENT_READABLE, &af->rEvent, argc, argv);
1149 static int aio_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1151 AioFile *af = Jim_CmdPrivData(interp);
1153 return aio_eventinfo(interp, af, JIM_EVENT_WRITABLE, &af->wEvent, argc, argv);
1156 static int aio_cmd_onexception(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1158 AioFile *af = Jim_CmdPrivData(interp);
1160 return aio_eventinfo(interp, af, JIM_EVENT_EXCEPTION, &af->eEvent, argc, argv);
1162 #endif
1164 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1165 static int aio_cmd_ssl(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1167 AioFile *af = Jim_CmdPrivData(interp);
1168 SSL *ssl;
1169 SSL_CTX *ssl_ctx;
1170 int fd, server = 0;
1172 if (argc == 5) {
1173 if (!Jim_CompareStringImmediate(interp, argv[2], "-server")) {
1174 return JIM_ERR;
1176 server = 1;
1178 else if (argc != 2) {
1179 Jim_WrongNumArgs(interp, 2, argv, "?-server cert priv?");
1180 return JIM_ERR;
1183 fd = fileno(af->fp);
1184 #if defined(HAVE_DUP)
1185 fd = dup(fd);
1186 if (fd < 0) {
1187 return JIM_ERR;
1189 #endif
1190 ssl_ctx = JimAioSslCtx(interp);
1191 if (ssl_ctx == NULL) {
1192 return JIM_ERR;
1195 ssl = SSL_new(ssl_ctx);
1196 if (ssl == NULL) {
1197 #if defined(HAVE_DUP)
1198 close(fd);
1199 #endif
1200 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1201 return JIM_ERR;
1204 SSL_set_cipher_list(ssl, "ALL");
1206 if (SSL_set_fd(ssl, fileno(af->fp)) == 0) {
1207 goto out;
1210 if (server) {
1211 if (SSL_use_certificate_file(ssl, Jim_String(argv[3]), SSL_FILETYPE_PEM) != 1) {
1212 goto out;
1215 if (SSL_use_PrivateKey_file(ssl, Jim_String(argv[4]), SSL_FILETYPE_PEM) != 1) {
1216 goto out;
1219 if (SSL_accept(ssl) != 1) {
1220 goto out;
1223 else {
1224 if (SSL_connect(ssl) != 1) {
1225 goto out;
1229 af = JimMakeChannel(interp, NULL, fd, NULL, "aio.sslstream%ld", af->addr_family, "r+");
1230 if (af == NULL) {
1231 goto out;
1234 af->ssl = ssl;
1235 af->fops = &ssl_fops;
1237 return JIM_OK;
1239 out:
1240 #if defined(HAVE_DUP)
1241 close(fd);
1242 #endif
1243 SSL_free(ssl);
1244 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1245 return JIM_ERR;
1248 static int aio_cmd_verify(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1250 AioFile *af = Jim_CmdPrivData(interp);
1251 int ret;
1253 if (!af->fops->verify) {
1254 return JIM_OK;
1257 ret = af->fops->verify(af);
1258 if (ret != JIM_OK) {
1259 if (JimCheckStreamError(interp, af)) {
1260 JimAioSetError(interp, af->filename);
1261 } else {
1262 Jim_SetResultString(interp, "failed to verify the connection authenticity", -1);
1265 return ret;
1267 #endif /* JIM_BOOTSTRAP */
1269 #if defined(HAVE_STRUCT_FLOCK) && !defined(JIM_BOOTSTRAP)
1270 static int aio_cmd_lock(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1272 AioFile *af = Jim_CmdPrivData(interp);
1273 struct flock fl;
1275 fl.l_start = 0;
1276 fl.l_len = 0;
1277 fl.l_type = F_WRLCK;
1278 fl.l_whence = SEEK_SET;
1280 switch (fcntl(af->fd, F_SETLK, &fl))
1282 case 0:
1283 Jim_SetResultInt(interp, 1);
1284 break;
1285 case -1:
1286 if (errno == EACCES || errno == EAGAIN)
1287 Jim_SetResultInt(interp, 0);
1288 else
1290 Jim_SetResultFormatted(interp, "lock failed: %s",
1291 strerror(errno));
1292 return JIM_ERR;
1294 break;
1295 default:
1296 Jim_SetResultInt(interp, 0);
1297 break;
1300 return JIM_OK;
1303 static int aio_cmd_unlock(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1305 AioFile *af = Jim_CmdPrivData(interp);
1306 struct flock fl;
1307 fl.l_start = 0;
1308 fl.l_len = 0;
1309 fl.l_type = F_UNLCK;
1310 fl.l_whence = SEEK_SET;
1312 Jim_SetResultInt(interp, fcntl(af->fd, F_SETLK, &fl) == 0);
1313 return JIM_OK;
1315 #endif /* JIM_BOOTSTRAP */
1317 #if defined(HAVE_TERMIOS_H) && !defined(JIM_BOOTSTRAP)
1318 static int aio_cmd_tty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1320 AioFile *af = Jim_CmdPrivData(interp);
1321 Jim_Obj *dictObjPtr;
1322 int ret;
1324 if (argc == 0) {
1325 /* get the current settings as a dictionary */
1326 dictObjPtr = Jim_GetTtySettings(interp, af->fd);
1327 if (dictObjPtr == NULL) {
1328 JimAioSetError(interp, NULL);
1329 return JIM_ERR;
1331 Jim_SetResult(interp, dictObjPtr);
1332 return JIM_OK;
1335 if (argc > 1) {
1336 /* Convert name value arguments to a dictionary */
1337 dictObjPtr = Jim_NewListObj(interp, argv, argc);
1339 else {
1340 /* The settings are already given as a list */
1341 dictObjPtr = argv[0];
1343 Jim_IncrRefCount(dictObjPtr);
1345 if (Jim_ListLength(interp, dictObjPtr) % 2) {
1346 /* Must be a valid dictionary */
1347 Jim_DecrRefCount(interp, dictObjPtr);
1348 return -1;
1351 ret = Jim_SetTtySettings(interp, af->fd, dictObjPtr);
1352 if (ret < 0) {
1353 JimAioSetError(interp, NULL);
1354 ret = JIM_ERR;
1356 Jim_DecrRefCount(interp, dictObjPtr);
1358 return ret;
1360 #endif /* JIM_BOOTSTRAP */
1362 static const jim_subcmd_type aio_command_table[] = {
1363 { "read",
1364 "?-nonewline? ?len?",
1365 aio_cmd_read,
1368 /* Description: Read and return bytes from the stream. To eof if no len. */
1370 { "copyto",
1371 "handle ?size?",
1372 aio_cmd_copy,
1375 /* Description: Copy up to 'size' bytes to the given filehandle, or to eof if no size. */
1377 { "gets",
1378 "?var?",
1379 aio_cmd_gets,
1382 /* Description: Read one line and return it or store it in the var */
1384 { "puts",
1385 "?-nonewline? str",
1386 aio_cmd_puts,
1389 /* Description: Write the string, with newline unless -nonewline */
1391 { "isatty",
1392 NULL,
1393 aio_cmd_isatty,
1396 /* Description: Is the file descriptor a tty? */
1398 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
1399 { "recvfrom",
1400 "len ?addrvar?",
1401 aio_cmd_recvfrom,
1404 /* Description: Receive up to 'len' bytes on the socket. Sets 'addrvar' with receive address, if set */
1406 { "sendto",
1407 "str address",
1408 aio_cmd_sendto,
1411 /* Description: Send 'str' to the given address (dgram only) */
1413 { "accept",
1414 "?addrvar?",
1415 aio_cmd_accept,
1418 /* Description: Server socket only: Accept a connection and return stream */
1420 { "listen",
1421 "backlog",
1422 aio_cmd_listen,
1425 /* Description: Set the listen backlog for server socket */
1427 #endif /* JIM_BOOTSTRAP */
1428 { "flush",
1429 NULL,
1430 aio_cmd_flush,
1433 /* Description: Flush the stream */
1435 { "eof",
1436 NULL,
1437 aio_cmd_eof,
1440 /* Description: Returns 1 if stream is at eof */
1442 { "close",
1443 "?r(ead)|w(rite)?",
1444 aio_cmd_close,
1447 JIM_MODFLAG_FULLARGV,
1448 /* Description: Closes the stream. */
1450 { "seek",
1451 "offset ?start|current|end",
1452 aio_cmd_seek,
1455 /* Description: Seeks in the stream (default 'current') */
1457 { "tell",
1458 NULL,
1459 aio_cmd_tell,
1462 /* Description: Returns the current seek position */
1464 { "filename",
1465 NULL,
1466 aio_cmd_filename,
1469 /* Description: Returns the original filename */
1471 #ifdef O_NDELAY
1472 { "ndelay",
1473 "?0|1?",
1474 aio_cmd_ndelay,
1477 /* Description: Set O_NDELAY (if arg). Returns current/new setting. */
1479 #endif
1480 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
1481 { "sockopt",
1482 "?opt 0|1?",
1483 aio_cmd_sockopt,
1486 /* Description: Return a dictionary of sockopts, or set the value of a sockopt */
1488 #endif
1489 #ifdef HAVE_FSYNC
1490 { "sync",
1491 NULL,
1492 aio_cmd_sync,
1495 /* Description: Flush and fsync() the stream */
1497 #endif
1498 { "buffering",
1499 "none|line|full",
1500 aio_cmd_buffering,
1503 /* Description: Sets buffering */
1505 #ifdef jim_ext_eventloop
1506 { "readable",
1507 "?readable-script?",
1508 aio_cmd_readable,
1511 /* Description: Returns script, or invoke readable-script when readable, {} to remove */
1513 { "writable",
1514 "?writable-script?",
1515 aio_cmd_writable,
1518 /* Description: Returns script, or invoke writable-script when writable, {} to remove */
1520 { "onexception",
1521 "?exception-script?",
1522 aio_cmd_onexception,
1525 /* Description: Returns script, or invoke exception-script when oob data, {} to remove */
1527 #endif
1528 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1529 { "ssl",
1530 "?-server cert priv?",
1531 aio_cmd_ssl,
1534 JIM_MODFLAG_FULLARGV
1535 /* Description: Wraps a stream socket with SSL/TLS and returns a new channel */
1537 { "verify",
1538 NULL,
1539 aio_cmd_verify,
1542 /* Description: Verifies the certificate of a SSL/TLS channel */
1544 #endif /* JIM_BOOTSTRAP */
1545 #if defined(HAVE_STRUCT_FLOCK) && !defined(JIM_BOOTSTRAP)
1546 { "lock",
1547 NULL,
1548 aio_cmd_lock,
1551 /* Description: Attempt to get a lock. */
1553 { "unlock",
1554 NULL,
1555 aio_cmd_unlock,
1558 /* Description: Relase a lock. */
1560 #endif /* JIM_BOOTSTRAP */
1561 #if defined(HAVE_TERMIOS_H) && !defined(JIM_BOOTSTRAP)
1562 { "tty",
1563 "?baud rate? ?data bits? ?stop bits? ?parity even|odd|none? ?handshake xonxoff|rtscts|none? ?input raw|cooked? ?output raw|cooked? ?vmin n? ?vtime n?",
1564 aio_cmd_tty,
1567 /* Description: Get or set tty settings - valid only on a tty */
1569 #endif /* JIM_BOOTSTRAP */
1570 { NULL }
1573 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1575 return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, aio_command_table, argc, argv), argc, argv);
1578 static int JimAioOpenCommand(Jim_Interp *interp, int argc,
1579 Jim_Obj *const *argv)
1581 const char *mode;
1583 if (argc != 2 && argc != 3) {
1584 Jim_WrongNumArgs(interp, 1, argv, "filename ?mode?");
1585 return JIM_ERR;
1588 mode = (argc == 3) ? Jim_String(argv[2]) : "r";
1590 #ifdef jim_ext_tclcompat
1592 const char *filename = Jim_String(argv[1]);
1594 /* If the filename starts with '|', use popen instead */
1595 if (*filename == '|') {
1596 Jim_Obj *evalObj[3];
1598 evalObj[0] = Jim_NewStringObj(interp, "::popen", -1);
1599 evalObj[1] = Jim_NewStringObj(interp, filename + 1, -1);
1600 evalObj[2] = Jim_NewStringObj(interp, mode, -1);
1602 return Jim_EvalObjVector(interp, 3, evalObj);
1605 #endif
1606 return JimMakeChannel(interp, NULL, -1, argv[1], "aio.handle%ld", 0, mode) ? JIM_OK : JIM_ERR;
1609 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1610 static void JimAioSslContextDelProc(struct Jim_Interp *interp, void *privData)
1612 SSL_CTX_free((SSL_CTX *)privData);
1613 ERR_free_strings();
1616 static SSL_CTX *JimAioSslCtx(Jim_Interp *interp)
1618 SSL_CTX *ssl_ctx = (SSL_CTX *)Jim_GetAssocData(interp, "ssl_ctx");
1619 if (ssl_ctx == NULL) {
1620 SSL_load_error_strings();
1621 SSL_library_init();
1622 ssl_ctx = SSL_CTX_new(TLSv1_2_method());
1623 if (ssl_ctx && SSL_CTX_set_default_verify_paths(ssl_ctx)) {
1624 SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
1625 Jim_SetAssocData(interp, "ssl_ctx", JimAioSslContextDelProc, ssl_ctx);
1626 } else {
1627 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1630 return ssl_ctx;
1632 #endif /* JIM_BOOTSTRAP */
1635 * Creates a channel for fh/fd/filename.
1637 * If fh is not NULL, uses that as the channel (and sets AIO_KEEPOPEN).
1638 * Otherwise, if fd is >= 0, uses that as the channel.
1639 * Otherwise opens 'filename' with mode 'mode'.
1641 * hdlfmt is a sprintf format for the filehandle. Anything with %ld at the end will do.
1642 * mode is used for open or fdopen.
1644 * Creates the command and sets the name as the current result.
1645 * Returns the AioFile pointer on sucess or NULL on failure.
1647 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
1648 const char *hdlfmt, int family, const char *mode)
1650 AioFile *af;
1651 char buf[AIO_CMD_LEN];
1652 int openFlags = 0;
1654 snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
1656 if (fh) {
1657 openFlags = AIO_KEEPOPEN;
1660 snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
1661 if (!filename) {
1662 filename = Jim_NewStringObj(interp, buf, -1);
1665 Jim_IncrRefCount(filename);
1667 if (fh == NULL) {
1668 #if !defined(JIM_ANSIC)
1669 if (fd >= 0) {
1670 fh = fdopen(fd, mode);
1672 else
1673 #endif
1674 fh = fopen(Jim_String(filename), mode);
1676 if (fh == NULL) {
1677 JimAioSetError(interp, filename);
1678 #if !defined(JIM_ANSIC)
1679 if (fd >= 0) {
1680 close(fd);
1682 #endif
1683 Jim_DecrRefCount(interp, filename);
1684 return NULL;
1688 /* Create the file command */
1689 af = Jim_Alloc(sizeof(*af));
1690 memset(af, 0, sizeof(*af));
1691 af->fp = fh;
1692 af->fd = fileno(fh);
1693 af->filename = filename;
1694 #ifdef FD_CLOEXEC
1695 if ((openFlags & AIO_KEEPOPEN) == 0) {
1696 (void)fcntl(af->fd, F_SETFD, FD_CLOEXEC);
1698 #endif
1699 af->openFlags = openFlags;
1700 af->addr_family = family;
1701 af->fops = &stdio_fops;
1702 af->ssl = NULL;
1704 Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc);
1706 /* Note that the command must use the global namespace, even if
1707 * the current namespace is something different
1709 Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, Jim_NewStringObj(interp, buf, -1)));
1711 return af;
1714 #if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && defined(HAVE_SYS_UN_H))
1716 * Create a pair of channels. e.g. from pipe() or socketpair()
1718 static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename,
1719 const char *hdlfmt, int family, const char *mode[2])
1721 if (JimMakeChannel(interp, NULL, p[0], filename, hdlfmt, family, mode[0])) {
1722 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
1723 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
1725 if (JimMakeChannel(interp, NULL, p[1], filename, hdlfmt, family, mode[1])) {
1726 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
1727 Jim_SetResult(interp, objPtr);
1728 return JIM_OK;
1732 /* Can only be here if fdopen() failed */
1733 close(p[0]);
1734 close(p[1]);
1735 JimAioSetError(interp, NULL);
1736 return JIM_ERR;
1738 #endif
1740 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
1742 static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1744 const char *hdlfmt = "aio.unknown%ld";
1745 const char *socktypes[] = {
1746 "unix",
1747 "unix.server",
1748 "dgram",
1749 "dgram.server",
1750 "stream",
1751 "stream.server",
1752 "pipe",
1753 "pair",
1754 NULL
1756 enum
1758 SOCK_UNIX,
1759 SOCK_UNIX_SERVER,
1760 SOCK_DGRAM_CLIENT,
1761 SOCK_DGRAM_SERVER,
1762 SOCK_STREAM_CLIENT,
1763 SOCK_STREAM_SERVER,
1764 SOCK_STREAM_PIPE,
1765 SOCK_STREAM_SOCKETPAIR,
1767 int socktype;
1768 int sock;
1769 const char *hostportarg = NULL;
1770 int res;
1771 int on = 1;
1772 const char *mode = "r+";
1773 int family = PF_INET;
1774 Jim_Obj *argv0 = argv[0];
1775 int ipv6 = 0;
1777 if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-ipv6")) {
1778 if (!IPV6) {
1779 Jim_SetResultString(interp, "ipv6 not supported", -1);
1780 return JIM_ERR;
1782 ipv6 = 1;
1783 family = PF_INET6;
1785 argc -= ipv6;
1786 argv += ipv6;
1788 if (argc < 2) {
1789 wrongargs:
1790 Jim_WrongNumArgs(interp, 1, &argv0, "?-ipv6? type ?address?");
1791 return JIM_ERR;
1794 if (Jim_GetEnum(interp, argv[1], socktypes, &socktype, "socket type", JIM_ERRMSG) != JIM_OK)
1795 return Jim_CheckShowCommands(interp, argv[1], socktypes);
1797 Jim_SetEmptyResult(interp);
1799 hdlfmt = "aio.sock%ld";
1801 if (argc > 2) {
1802 hostportarg = Jim_String(argv[2]);
1805 switch (socktype) {
1806 case SOCK_DGRAM_CLIENT:
1807 if (argc == 2) {
1808 /* No address, so an unconnected dgram socket */
1809 sock = socket(family, SOCK_DGRAM, 0);
1810 if (sock < 0) {
1811 JimAioSetError(interp, NULL);
1812 return JIM_ERR;
1814 break;
1816 /* fall through */
1817 case SOCK_STREAM_CLIENT:
1819 union sockaddr_any sa;
1820 int salen;
1822 if (argc != 3) {
1823 goto wrongargs;
1826 if (ipv6) {
1827 if (JimParseIPv6Address(interp, hostportarg, &sa, &salen) != JIM_OK) {
1828 return JIM_ERR;
1831 else if (JimParseIpAddress(interp, hostportarg, &sa, &salen) != JIM_OK) {
1832 return JIM_ERR;
1834 sock = socket(family, (socktype == SOCK_DGRAM_CLIENT) ? SOCK_DGRAM : SOCK_STREAM, 0);
1835 if (sock < 0) {
1836 JimAioSetError(interp, NULL);
1837 return JIM_ERR;
1839 res = connect(sock, &sa.sa, salen);
1840 if (res) {
1841 JimAioSetError(interp, argv[2]);
1842 close(sock);
1843 return JIM_ERR;
1846 break;
1848 case SOCK_STREAM_SERVER:
1849 case SOCK_DGRAM_SERVER:
1851 union sockaddr_any sa;
1852 int salen;
1854 if (argc != 3) {
1855 goto wrongargs;
1858 if (ipv6) {
1859 if (JimParseIPv6Address(interp, hostportarg, &sa, &salen) != JIM_OK) {
1860 return JIM_ERR;
1863 else if (JimParseIpAddress(interp, hostportarg, &sa, &salen) != JIM_OK) {
1864 return JIM_ERR;
1866 sock = socket(family, (socktype == SOCK_DGRAM_SERVER) ? SOCK_DGRAM : SOCK_STREAM, 0);
1867 if (sock < 0) {
1868 JimAioSetError(interp, NULL);
1869 return JIM_ERR;
1872 /* Enable address reuse */
1873 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
1875 res = bind(sock, &sa.sa, salen);
1876 if (res) {
1877 JimAioSetError(interp, argv[2]);
1878 close(sock);
1879 return JIM_ERR;
1881 if (socktype == SOCK_STREAM_SERVER) {
1882 res = listen(sock, 5);
1883 if (res) {
1884 JimAioSetError(interp, NULL);
1885 close(sock);
1886 return JIM_ERR;
1889 hdlfmt = "aio.socksrv%ld";
1891 break;
1893 #ifdef HAVE_SYS_UN_H
1894 case SOCK_UNIX:
1896 struct sockaddr_un sa;
1897 socklen_t len;
1899 if (argc != 3 || ipv6) {
1900 goto wrongargs;
1903 if (JimParseDomainAddress(interp, hostportarg, &sa) != JIM_OK) {
1904 JimAioSetError(interp, argv[2]);
1905 return JIM_ERR;
1907 family = PF_UNIX;
1908 sock = socket(PF_UNIX, SOCK_STREAM, 0);
1909 if (sock < 0) {
1910 JimAioSetError(interp, NULL);
1911 return JIM_ERR;
1913 len = strlen(sa.sun_path) + 1 + sizeof(sa.sun_family);
1914 res = connect(sock, (struct sockaddr *)&sa, len);
1915 if (res) {
1916 JimAioSetError(interp, argv[2]);
1917 close(sock);
1918 return JIM_ERR;
1920 hdlfmt = "aio.sockunix%ld";
1921 break;
1924 case SOCK_UNIX_SERVER:
1926 struct sockaddr_un sa;
1927 socklen_t len;
1929 if (argc != 3 || ipv6) {
1930 goto wrongargs;
1933 if (JimParseDomainAddress(interp, hostportarg, &sa) != JIM_OK) {
1934 JimAioSetError(interp, argv[2]);
1935 return JIM_ERR;
1937 family = PF_UNIX;
1938 sock = socket(PF_UNIX, SOCK_STREAM, 0);
1939 if (sock < 0) {
1940 JimAioSetError(interp, NULL);
1941 return JIM_ERR;
1943 len = strlen(sa.sun_path) + 1 + sizeof(sa.sun_family);
1944 res = bind(sock, (struct sockaddr *)&sa, len);
1945 if (res) {
1946 JimAioSetError(interp, argv[2]);
1947 close(sock);
1948 return JIM_ERR;
1950 res = listen(sock, 5);
1951 if (res) {
1952 JimAioSetError(interp, NULL);
1953 close(sock);
1954 return JIM_ERR;
1956 hdlfmt = "aio.sockunixsrv%ld";
1957 break;
1959 #endif
1961 #if defined(HAVE_SOCKETPAIR) && defined(HAVE_SYS_UN_H)
1962 case SOCK_STREAM_SOCKETPAIR:
1964 int p[2];
1965 static const char *mode[2] = { "r+", "r+" };
1967 if (argc != 2 || ipv6) {
1968 goto wrongargs;
1971 if (socketpair(PF_UNIX, SOCK_STREAM, 0, p) < 0) {
1972 JimAioSetError(interp, NULL);
1973 return JIM_ERR;
1975 return JimMakeChannelPair(interp, p, argv[1], "aio.sockpair%ld", PF_UNIX, mode);
1977 break;
1978 #endif
1980 #if defined(HAVE_PIPE)
1981 case SOCK_STREAM_PIPE:
1983 int p[2];
1984 static const char *mode[2] = { "r", "w" };
1986 if (argc != 2 || ipv6) {
1987 goto wrongargs;
1990 if (pipe(p) < 0) {
1991 JimAioSetError(interp, NULL);
1992 return JIM_ERR;
1995 return JimMakeChannelPair(interp, p, argv[1], "aio.pipe%ld", 0, mode);
1997 break;
1998 #endif
2000 default:
2001 Jim_SetResultString(interp, "Unsupported socket type", -1);
2002 return JIM_ERR;
2005 return JimMakeChannel(interp, NULL, sock, argv[1], hdlfmt, family, mode) ? JIM_OK : JIM_ERR;
2007 #endif /* JIM_BOOTSTRAP */
2010 * Returns the file descriptor of a writable, newly created temp file
2011 * or -1 on error.
2013 * On success, leaves the filename in the interpreter result, otherwise
2014 * leaves an error message.
2016 int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template)
2018 #ifdef HAVE_MKSTEMP
2019 int fd;
2020 mode_t mask;
2021 Jim_Obj *filenameObj;
2023 if (filename_template == NULL) {
2024 const char *tmpdir = getenv("TMPDIR");
2025 if (tmpdir == NULL || *tmpdir == '\0' || access(tmpdir, W_OK) != 0) {
2026 tmpdir = "/tmp/";
2028 filenameObj = Jim_NewStringObj(interp, tmpdir, -1);
2029 if (tmpdir[0] && tmpdir[strlen(tmpdir) - 1] != '/') {
2030 Jim_AppendString(interp, filenameObj, "/", 1);
2032 Jim_AppendString(interp, filenameObj, "tcl.tmp.XXXXXX", -1);
2034 else {
2035 filenameObj = Jim_NewStringObj(interp, filename_template, -1);
2038 /* Update the template name directly with the filename */
2039 mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
2040 fd = mkstemp(filenameObj->bytes);
2041 umask(mask);
2042 if (fd < 0) {
2043 JimAioSetError(interp, filenameObj);
2044 Jim_FreeNewObj(interp, filenameObj);
2045 return -1;
2048 Jim_SetResult(interp, filenameObj);
2049 return fd;
2050 #else
2051 Jim_SetResultString(interp, "platform has no tempfile support", -1);
2052 return -1;
2053 #endif
2056 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
2057 static int JimAioLoadSSLCertsCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2059 SSL_CTX *ssl_ctx;
2061 if (argc != 2) {
2062 Jim_WrongNumArgs(interp, 1, argv, "dir");
2063 return JIM_ERR;
2066 ssl_ctx = JimAioSslCtx(interp);
2067 if (!ssl_ctx) {
2068 return JIM_ERR;
2070 if (SSL_CTX_load_verify_locations(ssl_ctx, NULL, Jim_String(argv[1])) == 1) {
2071 return JIM_OK;
2073 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
2074 return JIM_ERR;
2076 #endif /* JIM_BOOTSTRAP */
2078 int Jim_aioInit(Jim_Interp *interp)
2080 if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG))
2081 return JIM_ERR;
2083 #if defined(JIM_SSL)
2084 Jim_CreateCommand(interp, "load_ssl_certs", JimAioLoadSSLCertsCommand, NULL, NULL);
2085 #endif
2087 Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL);
2088 #ifndef JIM_ANSIC
2089 Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL);
2090 #endif
2092 /* Create filehandles for stdin, stdout and stderr */
2093 JimMakeChannel(interp, stdin, -1, NULL, "stdin", 0, "r");
2094 JimMakeChannel(interp, stdout, -1, NULL, "stdout", 0, "w");
2095 JimMakeChannel(interp, stderr, -1, NULL, "stderr", 0, "w");
2097 return JIM_OK;