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
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.
40 #include "jimautoconf.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>
66 #include <openssl/ssl.h>
67 #include <openssl/err.h>
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 */
83 #define AIO_KEEPOPEN 1
94 #define JimCheckStreamError(interp, af) af->fops->error(af)
96 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
99 struct sockaddr_in sin
;
101 struct sockaddr_in6 sin6
;
105 #ifndef HAVE_INET_NTOP
106 const char *inet_ntop(int af
, const void *src
, char *dst
, int size
)
111 snprintf(dst
, size
, "%s", inet_ntoa(((struct sockaddr_in
*)src
)->sin_addr
));
115 #endif /* JIM_BOOTSTRAP */
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
);
128 typedef struct AioFile
133 int openFlags
; /* AIO_KEEPOPEN? keep FILE* */
140 const JimAioFopsType
*fops
;
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
)) {
164 /* EAGAIN and similar are not error conditions. Just treat them like eof */
165 if (feof(af
->fp
) || errno
== EAGAIN
|| errno
== EINTR
) {
169 if (errno
== ECONNRESET
) {
174 if (errno
!= ECONNABORTED
) {
181 static const char *stdio_strerror(struct AioFile
*af
)
183 return strerror(errno
);
186 static const JimAioFopsType stdio_fops
= {
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
)
212 for (i
= 0; i
< len
+ 1; i
++) {
213 if (SSL_read(af
->ssl
, &buf
[i
], 1) != 1) {
219 if (buf
[i
] == '\n') {
227 static int ssl_error(const struct AioFile
*af
)
229 if (ERR_peek_error() == 0) {
236 static const char *ssl_strerror(struct AioFile
*af
)
238 int err
= ERR_get_error();
241 return ERR_error_string(err
, NULL
);
244 /* should not happen */
245 return "unknown SSL error";
248 static int ssl_verify(struct AioFile
*af
)
252 cert
= SSL_get_peer_certificate(af
->ssl
);
258 if (SSL_get_verify_result(af
->ssl
) == X509_V_OK
) {
265 static const JimAioFopsType ssl_fops
= {
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
)
284 * An IPv6 addr/port looks like:
287 * [fe80::223:6cff:fe95:bdc0%en1]:2000
291 * Note that the "any" address is ::, which is the same as when no address is specified.
299 stport
= strrchr(hostport
, ':');
301 /* No : so, the whole thing is the port */
304 sthost
= Jim_StrDup(hostport
);
310 if (*hostport
== '[') {
311 /* This is a numeric ipv6 address */
312 char *pt
= strchr(++hostport
, ']');
314 sthost
= Jim_StrDupLen(hostport
, pt
- hostport
);
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
);
330 memcpy(&sa
->sin
, ai
->ai_addr
, ai
->ai_addrlen
);
331 *salen
= ai
->ai_addrlen
;
333 sa
->sin
.sin_port
= htons(atoi(stport
));
341 Jim_SetResultString(interp
, "ipv6 not supported", -1);
346 static int JimParseIpAddress(Jim_Interp
*interp
, const char *hostport
, union sockaddr_any
*sa
, int *salen
)
348 /* An IPv4 addr/port looks like:
353 * If the address is missing, INADDR_ANY is used.
354 * If the port is missing, 0 is used (only useful for server sockets).
360 stport
= strrchr(hostport
, ':');
362 /* No : so, the whole thing is the port */
364 sthost
= Jim_StrDup("0.0.0.0");
367 sthost
= Jim_StrDupLen(hostport
, stport
- hostport
);
372 #ifdef HAVE_GETADDRINFO
375 memset(&req
, '\0', sizeof(req
));
376 req
.ai_family
= PF_INET
;
378 if (getaddrinfo(sthost
, NULL
, &req
, &ai
)) {
382 memcpy(&sa
->sin
, ai
->ai_addr
, ai
->ai_addrlen
);
383 *salen
= ai
->ai_addrlen
;
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 */
401 sa
->sin
.sin_port
= htons(atoi(stport
));
406 Jim_SetResultFormatted(interp
, "Not a valid address: %s", hostport
);
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
);
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 */
431 if (sa
->sa
.sa_family
== PF_INET6
) {
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
));
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
));
445 /* recvfrom still works on unix domain sockets, etc */
449 return Jim_SetVariable(interp
, varObjPtr
, Jim_NewStringObj(interp
, addrbuf
, -1));
452 #endif /* JIM_BOOTSTRAP */
454 static const char *JimAioErrorString(AioFile
*af
)
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
);
467 Jim_SetResultFormatted(interp
, "%#s: %s", name
, JimAioErrorString(af
));
470 Jim_SetResultString(interp
, JimAioErrorString(af
), -1);
474 static void JimAioDelProc(Jim_Interp
*interp
, void *privData
)
476 AioFile
*af
= privData
;
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
);
488 if (af
->ssl
!= NULL
) {
493 if (!(af
->openFlags
& AIO_KEEPOPEN
)) {
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
];
506 jim_wide neededLen
= -1; /* -1 is "read as much as possible" */
508 if (argc
&& Jim_CompareStringImmediate(interp
, argv
[0], "-nonewline")) {
514 if (Jim_GetWide(interp
, argv
[0], &neededLen
) != JIM_OK
)
517 Jim_SetResultString(interp
, "invalid parameter: negative len", -1);
524 objPtr
= Jim_NewStringObj(interp
, NULL
, 0);
525 while (neededLen
!= 0) {
529 if (neededLen
== -1) {
530 readlen
= AIO_BUF_LEN
;
533 readlen
= (neededLen
> AIO_BUF_LEN
? AIO_BUF_LEN
: neededLen
);
535 retval
= af
->fops
->reader(af
, buf
, readlen
);
537 Jim_AppendString(interp
, objPtr
, buf
, retval
);
538 if (neededLen
!= -1) {
542 if (retval
!= readlen
)
545 /* Check for error conditions */
546 if (JimCheckStreamError(interp
, af
)) {
547 Jim_FreeNewObj(interp
, objPtr
);
552 const char *s
= Jim_GetString(objPtr
, &len
);
554 if (len
> 0 && s
[len
- 1] == '\n') {
556 objPtr
->bytes
[objPtr
->length
] = '\0';
559 Jim_SetResult(interp
, objPtr
);
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
);
575 FILE *Jim_AioFilehandle(Jim_Interp
*interp
, Jim_Obj
*command
)
579 af
= Jim_AioFile(interp
, command
);
587 static int aio_cmd_copy(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
589 AioFile
*af
= Jim_CmdPrivData(interp
);
591 jim_wide maxlen
= JIM_WIDE_MAX
;
592 AioFile
*outf
= Jim_AioFile(interp
, argv
[0]);
599 if (Jim_GetWide(interp
, argv
[1], &maxlen
) != JIM_OK
) {
604 while (count
< maxlen
) {
607 if (af
->fops
->reader(af
, &ch
, 1) != 1) {
610 if (outf
->fops
->writer(outf
, &ch
, 1) != 1) {
616 if (JimCheckStreamError(interp
, af
) || JimCheckStreamError(interp
, outf
)) {
620 Jim_SetResultInt(interp
, count
);
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
];
634 objPtr
= Jim_NewStringObj(interp
, NULL
, 0);
636 buf
[AIO_BUF_LEN
- 1] = '_';
638 if (af
->fops
->getline(af
, buf
, AIO_BUF_LEN
) == NULL
)
641 if (buf
[AIO_BUF_LEN
- 1] == '\0' && buf
[AIO_BUF_LEN
- 2] != '\n') {
642 Jim_AppendString(interp
, objPtr
, buf
, AIO_BUF_LEN
- 1);
647 if (len
&& (buf
[len
- 1] == '\n')) {
652 Jim_AppendString(interp
, objPtr
, buf
, len
);
657 if (JimCheckStreamError(interp
, af
)) {
659 Jim_FreeNewObj(interp
, objPtr
);
664 if (Jim_SetVariable(interp
, argv
[0], objPtr
) != JIM_OK
) {
665 Jim_FreeNewObj(interp
, objPtr
);
669 len
= Jim_Length(objPtr
);
671 if (len
== 0 && feof(af
->fp
)) {
672 /* On EOF returns -1 if varName was specified */
675 Jim_SetResultInt(interp
, len
);
678 Jim_SetResult(interp
, objPtr
);
683 static int aio_cmd_puts(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
685 AioFile
*af
= Jim_CmdPrivData(interp
);
691 if (!Jim_CompareStringImmediate(interp
, argv
[0], "-nonewline")) {
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) {
706 JimAioSetError(interp
, af
->filename
);
710 static int aio_cmd_isatty(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
713 AioFile
*af
= Jim_CmdPrivData(interp
);
714 Jim_SetResultInt(interp
, isatty(fileno(af
->fp
)));
716 Jim_SetResultInt(interp
, 0);
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
);
727 union sockaddr_any sa
;
729 socklen_t salen
= sizeof(sa
);
732 if (Jim_GetLong(interp
, argv
[0], &len
) != JIM_OK
) {
736 buf
= Jim_Alloc(len
+ 1);
738 rlen
= recvfrom(fileno(af
->fp
), buf
, len
, 0, &sa
.sa
, &salen
);
741 JimAioSetError(interp
, NULL
);
745 Jim_SetResult(interp
, Jim_NewStringObjNoAlloc(interp
, buf
, rlen
));
748 return JimFormatIpAddress(interp
, argv
[1], &sa
);
755 static int aio_cmd_sendto(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
757 AioFile
*af
= Jim_CmdPrivData(interp
);
761 union sockaddr_any sa
;
762 const char *addr
= Jim_String(argv
[1]);
765 if (IPV6
&& af
->addr_family
== PF_INET6
) {
766 if (JimParseIPv6Address(interp
, addr
, &sa
, &salen
) != JIM_OK
) {
770 else if (JimParseIpAddress(interp
, addr
, &sa
, &salen
) != JIM_OK
) {
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
);
778 JimAioSetError(interp
, NULL
);
781 Jim_SetResultInt(interp
, len
);
785 static int aio_cmd_accept(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
787 AioFile
*af
= Jim_CmdPrivData(interp
);
789 union sockaddr_any sa
;
790 socklen_t addrlen
= sizeof(sa
);
792 sock
= accept(af
->fd
, &sa
.sa
, &addrlen
);
794 JimAioSetError(interp
, NULL
);
799 if (JimFormatIpAddress(interp
, argv
[0], &sa
) != JIM_OK
) {
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
);
814 if (Jim_GetLong(interp
, argv
[0], &backlog
) != JIM_OK
) {
818 if (listen(af
->fd
, backlog
)) {
819 JimAioSetError(interp
, NULL
);
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
);
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
));
846 static int aio_cmd_close(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
849 #if !defined(JIM_ANSIC) && defined(HAVE_SHUTDOWN)
850 static const char * const options
[] = { "r", "w", NULL
};
851 enum { OPT_R
, OPT_W
, };
853 AioFile
*af
= Jim_CmdPrivData(interp
);
855 if (Jim_GetEnum(interp
, argv
[2], options
, &option
, NULL
, JIM_ERRMSG
) != JIM_OK
) {
858 if (shutdown(af
->fd
, option
== OPT_R
? SHUT_RD
: SHUT_WR
) == 0) {
861 JimAioSetError(interp
, NULL
);
863 Jim_SetResultString(interp
, "async close not supported", -1);
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
);
878 if (Jim_CompareStringImmediate(interp
, argv
[1], "start"))
880 else if (Jim_CompareStringImmediate(interp
, argv
[1], "current"))
882 else if (Jim_CompareStringImmediate(interp
, argv
[1], "end"))
888 if (Jim_GetWide(interp
, argv
[0], &offset
) != JIM_OK
) {
891 if (fseeko(af
->fp
, offset
, orig
) == -1) {
892 JimAioSetError(interp
, af
->filename
);
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
));
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
);
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
);
924 if (Jim_GetLong(interp
, argv
[0], &nb
) != JIM_OK
) {
933 (void)fcntl(af
->fd
, F_SETFL
, fmode
);
935 Jim_SetResultInt(interp
, (fmode
& O_NONBLOCK
) ? 1 : 0);
941 static int aio_cmd_sync(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
943 AioFile
*af
= Jim_CmdPrivData(interp
);
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
[] = {
969 if (Jim_GetEnum(interp
, argv
[0], options
, &option
, NULL
, JIM_ERRMSG
) != JIM_OK
) {
974 setvbuf(af
->fp
, NULL
, _IONBF
, 0);
977 setvbuf(af
->fp
, NULL
, _IOLBF
, BUFSIZ
);
980 setvbuf(af
->fp
, NULL
, _IOFBF
, BUFSIZ
);
986 #ifdef jim_ext_eventloop
987 static void JimAioFileEventFinalizer(Jim_Interp
*interp
, void *clientData
)
989 Jim_Obj
**objPtrPtr
= clientData
;
991 Jim_DecrRefCount(interp
, *objPtrPtr
);
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
)
1006 /* Return current script */
1007 if (*scriptHandlerObj
) {
1008 Jim_SetResult(interp
, *scriptHandlerObj
);
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 */
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
);
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
);
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
);
1065 if (!Jim_CompareStringImmediate(interp
, argv
[2], "-server")) {
1070 else if (argc
!= 2) {
1071 Jim_WrongNumArgs(interp
, 2, argv
, "?-server cert priv?");
1075 fd
= fileno(af
->fp
);
1076 #if defined(HAVE_DUP)
1082 ssl_ctx
= JimAioSslCtx(interp
);
1083 if (ssl_ctx
== NULL
) {
1087 ssl
= SSL_new(ssl_ctx
);
1089 #if defined(HAVE_DUP)
1092 Jim_SetResultString(interp
, ERR_error_string(ERR_get_error(), NULL
), -1);
1096 SSL_set_cipher_list(ssl
, "ALL");
1098 if (SSL_set_fd(ssl
, fileno(af
->fp
)) == 0) {
1103 if (SSL_use_certificate_file(ssl
, Jim_String(argv
[3]), SSL_FILETYPE_PEM
) != 1) {
1107 if (SSL_use_PrivateKey_file(ssl
, Jim_String(argv
[4]), SSL_FILETYPE_PEM
) != 1) {
1111 if (SSL_accept(ssl
) != 1) {
1116 if (SSL_connect(ssl
) != 1) {
1121 af
= JimMakeChannel(interp
, NULL
, fd
, NULL
, "aio.sslstream%ld", af
->addr_family
, "r+");
1127 af
->fops
= &ssl_fops
;
1132 #if defined(HAVE_DUP)
1136 Jim_SetResultString(interp
, ERR_error_string(ERR_get_error(), NULL
), -1);
1140 static int aio_cmd_verify(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1142 AioFile
*af
= Jim_CmdPrivData(interp
);
1145 if (!af
->fops
->verify
) {
1149 ret
= af
->fops
->verify(af
);
1150 if (ret
!= JIM_OK
) {
1151 if (JimCheckStreamError(interp
, af
)) {
1152 JimAioSetError(interp
, af
->filename
);
1154 Jim_SetResultString(interp
, "failed to verify the connection authenticity", -1);
1161 static const jim_subcmd_type aio_command_table
[] = {
1163 "?-nonewline? ?len?",
1167 /* Description: Read and return bytes from the stream. To eof if no len. */
1174 /* Description: Copy up to 'size' bytes to the given filehandle, or to eof if no size. */
1181 /* Description: Read one line and return it or store it in the var */
1188 /* Description: Write the string, with newline unless -nonewline */
1195 /* Description: Is the file descriptor a tty? */
1197 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
1203 /* Description: Receive up to 'len' bytes on the socket. Sets 'addrvar' with receive address, if set */
1210 /* Description: Send 'str' to the given address (dgram only) */
1217 /* Description: Server socket only: Accept a connection and return stream */
1224 /* Description: Set the listen backlog for server socket */
1226 #endif /* JIM_BOOTSTRAP */
1232 /* Description: Flush the stream */
1239 /* Description: Returns 1 if stream is at eof */
1246 JIM_MODFLAG_FULLARGV
,
1247 /* Description: Closes the stream. */
1250 "offset ?start|current|end",
1254 /* Description: Seeks in the stream (default 'current') */
1261 /* Description: Returns the current seek position */
1268 /* Description: Returns the original filename */
1276 /* Description: Set O_NDELAY (if arg). Returns current/new setting. */
1285 /* Description: Flush and fsync() the stream */
1293 /* Description: Sets buffering */
1295 #ifdef jim_ext_eventloop
1297 "?readable-script?",
1301 /* Description: Returns script, or invoke readable-script when readable, {} to remove */
1304 "?writable-script?",
1308 /* Description: Returns script, or invoke writable-script when writable, {} to remove */
1311 "?exception-script?",
1312 aio_cmd_onexception
,
1315 /* Description: Returns script, or invoke exception-script when oob data, {} to remove */
1318 #if defined(JIM_SSL)
1320 "?-server cert priv?",
1324 JIM_MODFLAG_FULLARGV
1325 /* Description: Wraps a stream socket with SSL/TLS and returns a new channel */
1332 /* Description: Verifies the certificate of a SSL/TLS channel */
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
)
1348 if (argc
!= 2 && argc
!= 3) {
1349 Jim_WrongNumArgs(interp
, 1, argv
, "filename ?mode?");
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
);
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
);
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();
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
);
1392 Jim_SetResultString(interp
, ERR_error_string(ERR_get_error(), NULL
), -1);
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
)
1416 char buf
[AIO_CMD_LEN
];
1419 snprintf(buf
, sizeof(buf
), hdlfmt
, Jim_GetId(interp
));
1422 openFlags
= AIO_KEEPOPEN
;
1425 snprintf(buf
, sizeof(buf
), hdlfmt
, Jim_GetId(interp
));
1427 filename
= Jim_NewStringObj(interp
, buf
, -1);
1430 Jim_IncrRefCount(filename
);
1433 #if !defined(JIM_ANSIC)
1435 fh
= fdopen(fd
, mode
);
1439 fh
= fopen(Jim_String(filename
), mode
);
1442 JimAioSetError(interp
, filename
);
1443 #if !defined(JIM_ANSIC)
1448 Jim_DecrRefCount(interp
, filename
);
1453 /* Create the file command */
1454 af
= Jim_Alloc(sizeof(*af
));
1455 memset(af
, 0, sizeof(*af
));
1457 af
->fd
= fileno(fh
);
1458 af
->filename
= filename
;
1460 if ((openFlags
& AIO_KEEPOPEN
) == 0) {
1461 (void)fcntl(af
->fd
, F_SETFD
, FD_CLOEXEC
);
1464 af
->openFlags
= openFlags
;
1465 af
->addr_family
= family
;
1466 af
->fops
= &stdio_fops
;
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)));
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
);
1497 /* Can only be here if fdopen() failed */
1500 JimAioSetError(interp
, NULL
);
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
[] = {
1530 SOCK_STREAM_SOCKETPAIR
,
1534 const char *hostportarg
= NULL
;
1537 const char *mode
= "r+";
1538 int family
= PF_INET
;
1539 Jim_Obj
*argv0
= argv
[0];
1542 if (argc
> 1 && Jim_CompareStringImmediate(interp
, argv
[1], "-ipv6")) {
1544 Jim_SetResultString(interp
, "ipv6 not supported", -1);
1555 Jim_WrongNumArgs(interp
, 1, &argv0
, "?-ipv6? type ?address?");
1559 if (Jim_GetEnum(interp
, argv
[1], socktypes
, &socktype
, "socket type", JIM_ERRMSG
) != JIM_OK
)
1562 Jim_SetEmptyResult(interp
);
1564 hdlfmt
= "aio.sock%ld";
1567 hostportarg
= Jim_String(argv
[2]);
1571 case SOCK_DGRAM_CLIENT
:
1573 /* No address, so an unconnected dgram socket */
1574 sock
= socket(family
, SOCK_DGRAM
, 0);
1576 JimAioSetError(interp
, NULL
);
1582 case SOCK_STREAM_CLIENT
:
1584 union sockaddr_any sa
;
1592 if (JimParseIPv6Address(interp
, hostportarg
, &sa
, &salen
) != JIM_OK
) {
1596 else if (JimParseIpAddress(interp
, hostportarg
, &sa
, &salen
) != JIM_OK
) {
1599 sock
= socket(family
, (socktype
== SOCK_DGRAM_CLIENT
) ? SOCK_DGRAM
: SOCK_STREAM
, 0);
1601 JimAioSetError(interp
, NULL
);
1604 res
= connect(sock
, &sa
.sa
, salen
);
1606 JimAioSetError(interp
, argv
[2]);
1613 case SOCK_STREAM_SERVER
:
1614 case SOCK_DGRAM_SERVER
:
1616 union sockaddr_any sa
;
1624 if (JimParseIPv6Address(interp
, hostportarg
, &sa
, &salen
) != JIM_OK
) {
1628 else if (JimParseIpAddress(interp
, hostportarg
, &sa
, &salen
) != JIM_OK
) {
1631 sock
= socket(family
, (socktype
== SOCK_DGRAM_SERVER
) ? SOCK_DGRAM
: SOCK_STREAM
, 0);
1633 JimAioSetError(interp
, NULL
);
1637 /* Enable address reuse */
1638 setsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, (void *)&on
, sizeof(on
));
1640 res
= bind(sock
, &sa
.sa
, salen
);
1642 JimAioSetError(interp
, argv
[2]);
1646 if (socktype
== SOCK_STREAM_SERVER
) {
1647 res
= listen(sock
, 5);
1649 JimAioSetError(interp
, NULL
);
1654 hdlfmt
= "aio.socksrv%ld";
1658 #ifdef HAVE_SYS_UN_H
1661 struct sockaddr_un sa
;
1664 if (argc
!= 3 || ipv6
) {
1668 if (JimParseDomainAddress(interp
, hostportarg
, &sa
) != JIM_OK
) {
1669 JimAioSetError(interp
, argv
[2]);
1673 sock
= socket(PF_UNIX
, SOCK_STREAM
, 0);
1675 JimAioSetError(interp
, NULL
);
1678 len
= strlen(sa
.sun_path
) + 1 + sizeof(sa
.sun_family
);
1679 res
= connect(sock
, (struct sockaddr
*)&sa
, len
);
1681 JimAioSetError(interp
, argv
[2]);
1685 hdlfmt
= "aio.sockunix%ld";
1689 case SOCK_UNIX_SERVER
:
1691 struct sockaddr_un sa
;
1694 if (argc
!= 3 || ipv6
) {
1698 if (JimParseDomainAddress(interp
, hostportarg
, &sa
) != JIM_OK
) {
1699 JimAioSetError(interp
, argv
[2]);
1703 sock
= socket(PF_UNIX
, SOCK_STREAM
, 0);
1705 JimAioSetError(interp
, NULL
);
1708 len
= strlen(sa
.sun_path
) + 1 + sizeof(sa
.sun_family
);
1709 res
= bind(sock
, (struct sockaddr
*)&sa
, len
);
1711 JimAioSetError(interp
, argv
[2]);
1715 res
= listen(sock
, 5);
1717 JimAioSetError(interp
, NULL
);
1721 hdlfmt
= "aio.sockunixsrv%ld";
1726 #if defined(HAVE_SOCKETPAIR) && defined(HAVE_SYS_UN_H)
1727 case SOCK_STREAM_SOCKETPAIR
:
1730 static const char *mode
[2] = { "r+", "r+" };
1732 if (argc
!= 2 || ipv6
) {
1736 if (socketpair(PF_UNIX
, SOCK_STREAM
, 0, p
) < 0) {
1737 JimAioSetError(interp
, NULL
);
1740 return JimMakeChannelPair(interp
, p
, argv
[1], "aio.sockpair%ld", PF_UNIX
, mode
);
1745 #if defined(HAVE_PIPE)
1746 case SOCK_STREAM_PIPE
:
1749 static const char *mode
[2] = { "r", "w" };
1751 if (argc
!= 2 || ipv6
) {
1756 JimAioSetError(interp
, NULL
);
1760 return JimMakeChannelPair(interp
, p
, argv
[1], "aio.pipe%ld", 0, mode
);
1766 Jim_SetResultString(interp
, "Unsupported socket type", -1);
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
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)
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) {
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);
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
);
1809 JimAioSetError(interp
, filenameObj
);
1810 Jim_FreeNewObj(interp
, filenameObj
);
1814 Jim_SetResult(interp
, filenameObj
);
1817 Jim_SetResultString(interp
, "platform has no tempfile support", -1);
1822 #if defined(JIM_SSL)
1823 static int JimAioLoadSSLCertsCommand(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1828 Jim_WrongNumArgs(interp
, 1, argv
, "dir");
1832 ssl_ctx
= JimAioSslCtx(interp
);
1836 if (SSL_CTX_load_verify_locations(ssl_ctx
, NULL
, Jim_String(argv
[1])) == 1) {
1839 Jim_SetResultString(interp
, ERR_error_string(ERR_get_error(), NULL
), -1);
1844 int Jim_aioInit(Jim_Interp
*interp
)
1846 if (Jim_PackageProvide(interp
, "aio", "1.0", JIM_ERRMSG
))
1849 #if defined(JIM_SSL)
1850 Jim_CreateCommand(interp
, "load_ssl_certs", JimAioLoadSSLCertsCommand
, NULL
, NULL
);
1853 Jim_CreateCommand(interp
, "open", JimAioOpenCommand
, NULL
, NULL
);
1855 Jim_CreateCommand(interp
, "socket", JimAioSockCommand
, NULL
, NULL
);
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");