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"
62 #include "jimiocompat.h"
64 #if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H)
65 #include <sys/socket.h>
66 #include <netinet/in.h>
67 #include <netinet/tcp.h>
68 #include <arpa/inet.h>
74 #elif defined (__MINGW32__)
75 /* currently mingw32 doesn't support sockets, but has pipe, fdopen */
81 #include <openssl/ssl.h>
82 #include <openssl/err.h>
89 #include "jim-eventloop.h"
90 #include "jim-subcmd.h"
92 #define AIO_CMD_LEN 32 /* e.g. aio.handleXXXXXX */
93 #define AIO_BUF_LEN 256 /* Can keep this small and rely on stdio buffering */
102 #define AIO_KEEPOPEN 1
103 #define AIO_NODELETE 2
106 #if defined(JIM_IPV6)
114 #if defined(HAVE_SYS_UN_H) && defined(PF_UNIX)
115 #define UNIX_SOCKETS 1
117 #define UNIX_SOCKETS 0
121 /* no fdopen() with ANSIC, so can't support these */
123 #undef HAVE_SOCKETPAIR
126 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
127 /* Avoid type punned pointers */
130 struct sockaddr_in sin
;
132 struct sockaddr_in6 sin6
;
135 struct sockaddr_un sun
;
139 #ifndef HAVE_INET_NTOP
140 const char *inet_ntop(int af
, const void *src
, char *dst
, int size
)
145 snprintf(dst
, size
, "%s", inet_ntoa(((struct sockaddr_in
*)src
)->sin_addr
));
149 #endif /* JIM_BOOTSTRAP */
154 int (*writer
)(struct AioFile
*af
, const char *buf
, int len
);
155 int (*reader
)(struct AioFile
*af
, char *buf
, int len
);
156 const char *(*getline
)(struct AioFile
*af
, char *buf
, int len
);
157 int (*error
)(const struct AioFile
*af
);
158 const char *(*strerror
)(struct AioFile
*af
);
159 int (*verify
)(struct AioFile
*af
);
160 int (*eof
)(struct AioFile
*af
);
161 int (*pending
)(struct AioFile
*af
);
164 typedef struct AioFile
169 int flags
; /* AIO_KEEPOPEN? keep FILE* */
176 const JimAioFopsType
*fops
;
179 static int stdio_writer(struct AioFile
*af
, const char *buf
, int len
)
181 return fwrite(buf
, 1, len
, af
->fp
);
184 static int stdio_reader(struct AioFile
*af
, char *buf
, int len
)
186 return fread(buf
, 1, len
, af
->fp
);
189 static const char *stdio_getline(struct AioFile
*af
, char *buf
, int len
)
191 return fgets(buf
, len
, af
->fp
);
194 static int stdio_error(const AioFile
*af
)
196 if (!ferror(af
->fp
)) {
200 /* EAGAIN and similar are not error conditions. Just treat them like eof */
201 if (feof(af
->fp
) || errno
== EAGAIN
|| errno
== EINTR
) {
205 if (errno
== ECONNRESET
) {
210 if (errno
== ECONNABORTED
) {
217 static const char *stdio_strerror(struct AioFile
*af
)
219 return strerror(errno
);
222 static int stdio_eof(struct AioFile
*af
)
227 static const JimAioFopsType stdio_fops
= {
238 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
240 static SSL_CTX
*JimAioSslCtx(Jim_Interp
*interp
);
242 static int ssl_writer(struct AioFile
*af
, const char *buf
, int len
)
244 return SSL_write(af
->ssl
, buf
, len
);
247 static int ssl_pending(struct AioFile
*af
)
249 return SSL_pending(af
->ssl
);
252 static int ssl_reader(struct AioFile
*af
, char *buf
, int len
)
254 int ret
= SSL_read(af
->ssl
, buf
, len
);
255 switch (SSL_get_error(af
->ssl
, ret
)) {
258 case SSL_ERROR_SYSCALL
:
259 case SSL_ERROR_ZERO_RETURN
:
260 if (errno
!= EAGAIN
) {
261 af
->flags
|= AIO_EOF
;
266 if (errno
== EAGAIN
) {
269 af
->flags
|= AIO_EOF
;
274 static int ssl_eof(struct AioFile
*af
)
276 return (af
->flags
& AIO_EOF
);
279 static const char *ssl_getline(struct AioFile
*af
, char *buf
, int len
)
282 for (i
= 0; i
< len
- 1 && !ssl_eof(af
); i
++) {
283 int ret
= ssl_reader(af
, &buf
[i
], 1);
287 if (buf
[i
] == '\n') {
293 if (i
== 0 && ssl_eof(af
)) {
299 static int ssl_error(const struct AioFile
*af
)
301 int ret
= SSL_get_error(af
->ssl
, 0);
302 /* XXX should we be following the same logic as ssl_reader() here? */
303 if (ret
== SSL_ERROR_ZERO_RETURN
|| ret
== SSL_ERROR_NONE
) {
306 if (ret
== SSL_ERROR_SYSCALL
) {
307 return stdio_error(af
);
312 static const char *ssl_strerror(struct AioFile
*af
)
314 int err
= ERR_get_error();
317 return ERR_error_string(err
, NULL
);
320 return stdio_strerror(af
);
324 static int ssl_verify(struct AioFile
*af
)
328 cert
= SSL_get_peer_certificate(af
->ssl
);
334 if (SSL_get_verify_result(af
->ssl
) == X509_V_OK
) {
341 static const JimAioFopsType ssl_fops
= {
351 #endif /* JIM_BOOTSTRAP */
353 static int JimAioSubCmdProc(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
);
354 static AioFile
*JimMakeChannel(Jim_Interp
*interp
, FILE *fh
, int fd
, Jim_Obj
*filename
,
355 const char *hdlfmt
, int family
, const char *mode
, int flags
);
357 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
358 #ifndef HAVE_GETADDRINFO
360 * Poor man's getaddrinfo().
361 * hints->ai_family must be set and must be PF_INET or PF_INET6
362 * Only returns the first matching result.
363 * servname must be numeric.
367 socklen_t ai_addrlen
;
368 struct sockaddr
*ai_addr
; /* simply points to ai_storage */
369 union sockaddr_any ai_storage
;
372 static int getaddrinfo(const char *hostname
, const char *servname
,
373 const struct addrinfo
*hints
, struct addrinfo
**res
)
377 unsigned long port
= strtoul(servname
, &end
, 10);
378 if (port
== 0 || port
> 65536 || *end
) {
383 if ((he
= gethostbyname(hostname
)) != NULL
) {
385 for (i
= 0; he
->h_addr_list
[i
]; i
++) {
386 if (he
->h_addrtype
== hints
->ai_family
) {
387 struct addrinfo
*ai
= malloc(sizeof(*ai
));
388 memset(ai
, 0, sizeof(*ai
));
389 ai
->ai_family
= he
->h_addrtype
;
390 ai
->ai_addr
= &ai
->ai_storage
.sa
;
391 if (ai
->ai_family
== PF_INET
) {
392 ai
->ai_addrlen
= sizeof(ai
->ai_storage
.sin
);
393 ai
->ai_storage
.sin
.sin_family
= he
->h_addrtype
;
394 assert(sizeof(ai
->ai_storage
.sin
.sin_addr
) == he
->h_length
);
395 memcpy(&ai
->ai_storage
.sin
.sin_addr
, he
->h_addr_list
[i
], he
->h_length
);
396 ai
->ai_storage
.sin
.sin_port
= htons(port
);
400 ai
->ai_addrlen
= sizeof(ai
->ai_storage
.sin6
);
401 ai
->ai_storage
.sin6
.sin6_family
= he
->h_addrtype
;
402 assert(sizeof(ai
->ai_storage
.sin6
.sin6_addr
) == he
->h_length
);
403 memcpy(&ai
->ai_storage
.sin6
.sin6_addr
, he
->h_addr_list
[i
], he
->h_length
);
404 ai
->ai_storage
.sin6
.sin6_port
= htons(port
);
415 static void freeaddrinfo(struct addrinfo
*ai
)
421 static int JimParseIPv6Address(Jim_Interp
*interp
, const char *hostport
, union sockaddr_any
*sa
, socklen_t
*salen
)
425 * An IPv6 addr/port looks like:
428 * [fe80::223:6cff:fe95:bdc0%en1]:2000
432 * Note that the "any" address is ::, which is the same as when no address is specified.
440 stport
= strrchr(hostport
, ':');
442 /* No : so, the whole thing is the port */
445 sthost
= Jim_StrDup(hostport
);
451 if (*hostport
== '[') {
452 /* This is a numeric ipv6 address */
453 char *pt
= strchr(++hostport
, ']');
455 sthost
= Jim_StrDupLen(hostport
, pt
- hostport
);
460 sthost
= Jim_StrDupLen(hostport
, stport
- hostport
- 1);
463 memset(&req
, '\0', sizeof(req
));
464 req
.ai_family
= PF_INET6
;
466 if (getaddrinfo(sthost
, stport
, &req
, &ai
)) {
467 Jim_SetResultFormatted(interp
, "Not a valid address: %s:%s", sthost
, stport
);
471 memcpy(&sa
->sin6
, ai
->ai_addr
, ai
->ai_addrlen
);
472 *salen
= ai
->ai_addrlen
;
479 Jim_SetResultString(interp
, "ipv6 not supported", -1);
484 static int JimParseIpAddress(Jim_Interp
*interp
, const char *hostport
, union sockaddr_any
*sa
, socklen_t
*salen
)
486 /* An IPv4 addr/port looks like:
491 * If the address is missing, INADDR_ANY is used.
492 * If the port is missing, 0 is used (only useful for server sockets).
500 stport
= strrchr(hostport
, ':');
502 /* No : so, the whole thing is the port */
504 sthost
= Jim_StrDup("0.0.0.0");
507 sthost
= Jim_StrDupLen(hostport
, stport
- hostport
);
511 memset(&req
, '\0', sizeof(req
));
512 req
.ai_family
= PF_INET
;
514 if (getaddrinfo(sthost
, stport
, &req
, &ai
)) {
518 memcpy(&sa
->sin
, ai
->ai_addr
, ai
->ai_addrlen
);
519 *salen
= ai
->ai_addrlen
;
525 Jim_SetResultFormatted(interp
, "Not a valid address: %s", hostport
);
532 static int JimParseDomainAddress(Jim_Interp
*interp
, const char *path
, union sockaddr_any
*sa
, socklen_t
*salen
)
534 sa
->sun
.sun_family
= PF_UNIX
;
535 snprintf(sa
->sun
.sun_path
, sizeof(sa
->sun
.sun_path
), "%s", path
);
536 *salen
= strlen(sa
->sun
.sun_path
) + 1 + sizeof(sa
->sun
.sun_family
);
542 static int JimParseSocketAddress(Jim_Interp
*interp
, int family
, const char *addr
, union sockaddr_any
*sa
, socklen_t
*salen
)
547 return JimParseDomainAddress(interp
, addr
, sa
, salen
);
550 return JimParseIPv6Address(interp
, addr
, sa
, salen
);
552 return JimParseIpAddress(interp
, addr
, sa
, salen
);
558 * Format that address in 'sa' as a string and return it as a zero-refcount object.
561 static Jim_Obj
*JimFormatSocketAddress(Jim_Interp
*interp
, const union sockaddr_any
*sa
, socklen_t salen
)
563 /* INET6_ADDRSTRLEN is 46. Add some for [] and port */
565 const char *addr
= addrbuf
;
568 switch (sa
->sa
.sa_family
) {
571 addr
= sa
->sun
.sun_path
;
572 addrlen
= salen
- 1 - sizeof(sa
->sun
.sun_family
);
581 /* Allow 9 for []:65535\0 */
582 inet_ntop(sa
->sa
.sa_family
, &sa
->sin6
.sin6_addr
, addrbuf
+ 1, sizeof(addrbuf
) - 9);
583 snprintf(addrbuf
+ strlen(addrbuf
), 8, "]:%d", ntohs(sa
->sin6
.sin6_port
));
587 /* Allow 7 for :65535\0 */
588 inet_ntop(sa
->sa
.sa_family
, &sa
->sin
.sin_addr
, addrbuf
, sizeof(addrbuf
) - 7);
589 snprintf(addrbuf
+ strlen(addrbuf
), 7, ":%d", ntohs(sa
->sin
.sin_port
));
593 /* Otherwise just an empty address */
598 return Jim_NewStringObj(interp
, addr
, addrlen
);
601 static int JimSetVariableSocketAddress(Jim_Interp
*interp
, Jim_Obj
*varObjPtr
, const union sockaddr_any
*sa
, socklen_t salen
)
604 Jim_Obj
*objPtr
= JimFormatSocketAddress(interp
, sa
, salen
);
605 Jim_IncrRefCount(objPtr
);
606 ret
= Jim_SetVariable(interp
, varObjPtr
, objPtr
);
607 Jim_DecrRefCount(interp
, objPtr
);
611 static Jim_Obj
*aio_sockname(Jim_Interp
*interp
, AioFile
*af
)
613 union sockaddr_any sa
;
614 socklen_t salen
= sizeof(sa
);
616 if (getsockname(af
->fd
, &sa
.sa
, &salen
) < 0) {
619 return JimFormatSocketAddress(interp
, &sa
, salen
);
621 #endif /* JIM_BOOTSTRAP */
623 static const char *JimAioErrorString(AioFile
*af
)
626 return af
->fops
->strerror(af
);
628 return strerror(errno
);
631 static void JimAioSetError(Jim_Interp
*interp
, Jim_Obj
*name
)
633 AioFile
*af
= Jim_CmdPrivData(interp
);
636 Jim_SetResultFormatted(interp
, "%#s: %s", name
, JimAioErrorString(af
));
639 Jim_SetResultString(interp
, JimAioErrorString(af
), -1);
643 static int JimCheckStreamError(Jim_Interp
*interp
, AioFile
*af
)
645 int ret
= af
->fops
->error(af
);
647 JimAioSetError(interp
, af
->filename
);
652 static void JimAioDelProc(Jim_Interp
*interp
, void *privData
)
654 AioFile
*af
= privData
;
659 if (af
->addr_family
== PF_UNIX
&& (af
->flags
& AIO_NODELETE
) == 0) {
660 /* If this is bound, delete the socket file now */
661 Jim_Obj
*filenameObj
= aio_sockname(interp
, af
);
663 if (Jim_Length(filenameObj
)) {
664 remove(Jim_String(filenameObj
));
666 Jim_FreeNewObj(interp
, filenameObj
);
671 Jim_DecrRefCount(interp
, af
->filename
);
673 #ifdef jim_ext_eventloop
674 /* remove all existing EventHandlers */
675 Jim_DeleteFileHandler(interp
, af
->fd
, JIM_EVENT_READABLE
| JIM_EVENT_WRITABLE
| JIM_EVENT_EXCEPTION
);
679 if (af
->ssl
!= NULL
) {
683 if (!(af
->flags
& AIO_KEEPOPEN
)) {
690 static int aio_cmd_read(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
692 AioFile
*af
= Jim_CmdPrivData(interp
);
693 char buf
[AIO_BUF_LEN
];
697 jim_wide neededLen
= -1; /* -1 is "read as much as possible" */
698 static const char * const options
[] = { "-pending", "-nonewline", NULL
};
699 enum { OPT_PENDING
, OPT_NONEWLINE
};
703 if (*Jim_String(argv
[0]) == '-') {
704 if (Jim_GetEnum(interp
, argv
[0], options
, &option
, NULL
, JIM_ERRMSG
) != JIM_OK
) {
709 if (!af
->fops
->pending
) {
710 Jim_SetResultString(interp
, "-pending not supported on this connection type", -1);
721 if (Jim_GetWide(interp
, argv
[0], &neededLen
) != JIM_OK
)
724 Jim_SetResultString(interp
, "invalid parameter: negative len", -1);
734 objPtr
= Jim_NewStringObj(interp
, NULL
, 0);
735 while (neededLen
!= 0) {
739 if (neededLen
== -1) {
740 readlen
= AIO_BUF_LEN
;
743 readlen
= (neededLen
> AIO_BUF_LEN
? AIO_BUF_LEN
: neededLen
);
745 retval
= af
->fops
->reader(af
, buf
, pending
? 1 : readlen
);
747 Jim_AppendString(interp
, objPtr
, buf
, retval
);
748 if (neededLen
!= -1) {
752 /* If pending was specified, after we do the initial read,
753 * we do a second read to fetch any buffered data
755 neededLen
= af
->fops
->pending(af
);
762 /* Check for error conditions */
763 if (JimCheckStreamError(interp
, af
)) {
764 Jim_FreeNewObj(interp
, objPtr
);
769 const char *s
= Jim_GetString(objPtr
, &len
);
771 if (len
> 0 && s
[len
- 1] == '\n') {
773 objPtr
->bytes
[objPtr
->length
] = '\0';
776 Jim_SetResult(interp
, objPtr
);
780 AioFile
*Jim_AioFile(Jim_Interp
*interp
, Jim_Obj
*command
)
782 Jim_Cmd
*cmdPtr
= Jim_GetCommand(interp
, command
, JIM_ERRMSG
);
784 /* XXX: There ought to be a supported API for this */
785 if (cmdPtr
&& !cmdPtr
->isproc
&& cmdPtr
->u
.native
.cmdProc
== JimAioSubCmdProc
) {
786 return (AioFile
*) cmdPtr
->u
.native
.privData
;
788 Jim_SetResultFormatted(interp
, "Not a filehandle: \"%#s\"", command
);
792 FILE *Jim_AioFilehandle(Jim_Interp
*interp
, Jim_Obj
*command
)
796 af
= Jim_AioFile(interp
, command
);
804 static int aio_cmd_getfd(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
806 AioFile
*af
= Jim_CmdPrivData(interp
);
809 Jim_SetResultInt(interp
, fileno(af
->fp
));
814 static int aio_cmd_copy(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
816 AioFile
*af
= Jim_CmdPrivData(interp
);
818 jim_wide maxlen
= JIM_WIDE_MAX
;
819 AioFile
*outf
= Jim_AioFile(interp
, argv
[0]);
826 if (Jim_GetWide(interp
, argv
[1], &maxlen
) != JIM_OK
) {
831 while (count
< maxlen
) {
832 /* A reasonable compromise between stack size and speed */
833 char buf
[AIO_BUF_LEN
];
834 jim_wide len
= maxlen
- count
;
835 if (len
> sizeof(buf
)) {
839 len
= af
->fops
->reader(af
, buf
, len
);
843 if (outf
->fops
->writer(outf
, buf
, len
) != len
) {
849 if (JimCheckStreamError(interp
, af
) || JimCheckStreamError(interp
, outf
)) {
853 Jim_SetResultInt(interp
, count
);
858 static int aio_cmd_gets(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
860 AioFile
*af
= Jim_CmdPrivData(interp
);
861 char buf
[AIO_BUF_LEN
];
867 objPtr
= Jim_NewStringObj(interp
, NULL
, 0);
869 buf
[AIO_BUF_LEN
- 1] = '_';
871 if (af
->fops
->getline(af
, buf
, AIO_BUF_LEN
) == NULL
)
874 if (buf
[AIO_BUF_LEN
- 1] == '\0' && buf
[AIO_BUF_LEN
- 2] != '\n') {
875 Jim_AppendString(interp
, objPtr
, buf
, AIO_BUF_LEN
- 1);
880 if (len
&& (buf
[len
- 1] == '\n')) {
885 Jim_AppendString(interp
, objPtr
, buf
, len
);
890 if (JimCheckStreamError(interp
, af
)) {
892 Jim_FreeNewObj(interp
, objPtr
);
897 if (Jim_SetVariable(interp
, argv
[0], objPtr
) != JIM_OK
) {
898 Jim_FreeNewObj(interp
, objPtr
);
902 len
= Jim_Length(objPtr
);
904 if (len
== 0 && af
->fops
->eof(af
)) {
905 /* On EOF returns -1 if varName was specified */
908 Jim_SetResultInt(interp
, len
);
911 Jim_SetResult(interp
, objPtr
);
916 static int aio_cmd_puts(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
918 AioFile
*af
= Jim_CmdPrivData(interp
);
924 if (!Jim_CompareStringImmediate(interp
, argv
[0], "-nonewline")) {
933 wdata
= Jim_GetString(strObj
, &wlen
);
934 if (af
->fops
->writer(af
, wdata
, wlen
) == wlen
) {
935 if (argc
== 2 || af
->fops
->writer(af
, "\n", 1) == 1) {
939 JimAioSetError(interp
, af
->filename
);
943 static int aio_cmd_isatty(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
946 AioFile
*af
= Jim_CmdPrivData(interp
);
947 Jim_SetResultInt(interp
, isatty(fileno(af
->fp
)));
949 Jim_SetResultInt(interp
, 0);
955 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
956 static int aio_cmd_recvfrom(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
958 AioFile
*af
= Jim_CmdPrivData(interp
);
960 union sockaddr_any sa
;
962 socklen_t salen
= sizeof(sa
);
965 if (Jim_GetLong(interp
, argv
[0], &len
) != JIM_OK
) {
969 buf
= Jim_Alloc(len
+ 1);
971 rlen
= recvfrom(fileno(af
->fp
), buf
, len
, 0, &sa
.sa
, &salen
);
974 JimAioSetError(interp
, NULL
);
978 Jim_SetResult(interp
, Jim_NewStringObjNoAlloc(interp
, buf
, rlen
));
981 return JimSetVariableSocketAddress(interp
, argv
[1], &sa
, salen
);
988 static int aio_cmd_sendto(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
990 AioFile
*af
= Jim_CmdPrivData(interp
);
994 union sockaddr_any sa
;
995 const char *addr
= Jim_String(argv
[1]);
998 if (JimParseSocketAddress(interp
, af
->addr_family
, addr
, &sa
, &salen
) != JIM_OK
) {
1001 wdata
= Jim_GetString(argv
[0], &wlen
);
1003 /* Note that we don't validate the socket type. Rely on sendto() failing if appropriate */
1004 len
= sendto(fileno(af
->fp
), wdata
, wlen
, 0, &sa
.sa
, salen
);
1006 JimAioSetError(interp
, NULL
);
1009 Jim_SetResultInt(interp
, len
);
1013 static int aio_cmd_accept(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1015 AioFile
*af
= Jim_CmdPrivData(interp
);
1017 union sockaddr_any sa
;
1018 socklen_t salen
= sizeof(sa
);
1020 sock
= accept(af
->fd
, &sa
.sa
, &salen
);
1022 JimAioSetError(interp
, NULL
);
1027 if (JimSetVariableSocketAddress(interp
, argv
[0], &sa
, salen
) != JIM_OK
) {
1032 /* Create the file command */
1033 return JimMakeChannel(interp
, NULL
, sock
, Jim_NewStringObj(interp
, "accept", -1),
1034 "aio.sockstream%ld", af
->addr_family
, "r+", AIO_NODELETE
) ? JIM_OK
: JIM_ERR
;
1037 static int aio_cmd_sockname(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1039 AioFile
*af
= Jim_CmdPrivData(interp
);
1040 Jim_Obj
*objPtr
= aio_sockname(interp
, af
);
1042 if (objPtr
== NULL
) {
1043 JimAioSetError(interp
, NULL
);
1046 Jim_SetResult(interp
, objPtr
);
1050 static int aio_cmd_peername(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1052 AioFile
*af
= Jim_CmdPrivData(interp
);
1053 union sockaddr_any sa
;
1054 socklen_t salen
= sizeof(sa
);
1056 if (getpeername(af
->fd
, &sa
.sa
, &salen
) < 0) {
1057 JimAioSetError(interp
, NULL
);
1060 Jim_SetResult(interp
, JimFormatSocketAddress(interp
, &sa
, salen
));
1064 static int aio_cmd_listen(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1066 AioFile
*af
= Jim_CmdPrivData(interp
);
1069 if (Jim_GetLong(interp
, argv
[0], &backlog
) != JIM_OK
) {
1073 if (listen(af
->fd
, backlog
)) {
1074 JimAioSetError(interp
, NULL
);
1080 #endif /* JIM_BOOTSTRAP */
1082 static int aio_cmd_flush(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1084 AioFile
*af
= Jim_CmdPrivData(interp
);
1086 if (fflush(af
->fp
) == EOF
) {
1087 JimAioSetError(interp
, af
->filename
);
1093 static int aio_cmd_eof(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1095 AioFile
*af
= Jim_CmdPrivData(interp
);
1097 Jim_SetResultInt(interp
, !!af
->fops
->eof(af
));
1101 static int aio_cmd_close(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1105 #if defined(HAVE_SOCKETS)
1106 static const char * const options
[] = { "r", "w", "-nodelete", NULL
};
1107 enum { OPT_R
, OPT_W
, OPT_NODELETE
};
1108 AioFile
*af
= Jim_CmdPrivData(interp
);
1110 if (Jim_GetEnum(interp
, argv
[2], options
, &option
, NULL
, JIM_ERRMSG
) != JIM_OK
) {
1115 #if defined(HAVE_SHUTDOWN)
1118 if (shutdown(af
->fd
, option
== OPT_R
? SHUT_RD
: SHUT_WR
) == 0) {
1121 JimAioSetError(interp
, NULL
);
1126 if (af
->addr_family
== PF_UNIX
) {
1127 af
->flags
|= AIO_NODELETE
;
1133 Jim_SetResultString(interp
, "not supported", -1);
1138 return Jim_DeleteCommand(interp
, argv
[0]);
1141 static int aio_cmd_seek(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1143 AioFile
*af
= Jim_CmdPrivData(interp
);
1144 int orig
= SEEK_SET
;
1148 if (Jim_CompareStringImmediate(interp
, argv
[1], "start"))
1150 else if (Jim_CompareStringImmediate(interp
, argv
[1], "current"))
1152 else if (Jim_CompareStringImmediate(interp
, argv
[1], "end"))
1158 if (Jim_GetWide(interp
, argv
[0], &offset
) != JIM_OK
) {
1161 if (fseeko(af
->fp
, offset
, orig
) == -1) {
1162 JimAioSetError(interp
, af
->filename
);
1168 static int aio_cmd_tell(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1170 AioFile
*af
= Jim_CmdPrivData(interp
);
1172 Jim_SetResultInt(interp
, ftello(af
->fp
));
1176 static int aio_cmd_filename(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1178 AioFile
*af
= Jim_CmdPrivData(interp
);
1180 Jim_SetResult(interp
, af
->filename
);
1185 static int aio_cmd_ndelay(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1187 AioFile
*af
= Jim_CmdPrivData(interp
);
1189 int fmode
= fcntl(af
->fd
, F_GETFL
);
1194 if (Jim_GetLong(interp
, argv
[0], &nb
) != JIM_OK
) {
1203 (void)fcntl(af
->fd
, F_SETFL
, fmode
);
1205 Jim_SetResultInt(interp
, (fmode
& O_NONBLOCK
) ? 1 : 0);
1210 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
1211 #define SOCKOPT_BOOL 0
1212 #define SOCKOPT_INT 1
1213 #define SOCKOPT_TIMEVAL 2 /* not currently supported */
1215 static const struct sockopt_def
{
1219 int type
; /* SOCKOPT_xxx */
1223 { "broadcast", SOL_SOCKET
, SO_BROADCAST
},
1226 { "debug", SOL_SOCKET
, SO_DEBUG
},
1229 { "keepalive", SOL_SOCKET
, SO_KEEPALIVE
},
1232 { "nosigpipe", SOL_SOCKET
, SO_NOSIGPIPE
},
1235 { "oobinline", SOL_SOCKET
, SO_OOBINLINE
},
1238 { "sndbuf", SOL_SOCKET
, SO_SNDBUF
, SOCKOPT_INT
},
1241 { "rcvbuf", SOL_SOCKET
, SO_RCVBUF
, SOCKOPT_INT
},
1243 #if 0 && defined(SO_SNDTIMEO)
1244 { "sndtimeo", SOL_SOCKET
, SO_SNDTIMEO
, SOCKOPT_TIMEVAL
},
1246 #if 0 && defined(SO_RCVTIMEO)
1247 { "rcvtimeo", SOL_SOCKET
, SO_RCVTIMEO
, SOCKOPT_TIMEVAL
},
1252 { "tcp_nodelay", IPPROTO_TCP
, TCP_NODELAY
},
1257 static int aio_cmd_sockopt(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1259 AioFile
*af
= Jim_CmdPrivData(interp
);
1263 Jim_Obj
*dictObjPtr
= Jim_NewListObj(interp
, NULL
, 0);
1264 for (i
= 0; i
< sizeof(sockopts
) / sizeof(*sockopts
); i
++) {
1266 socklen_t len
= sizeof(value
);
1267 if (getsockopt(af
->fd
, sockopts
[i
].level
, sockopts
[i
].opt
, (void *)&value
, &len
) == 0) {
1268 if (sockopts
[i
].type
== SOCKOPT_BOOL
) {
1271 Jim_ListAppendElement(interp
, dictObjPtr
, Jim_NewStringObj(interp
, sockopts
[i
].name
, -1));
1272 Jim_ListAppendElement(interp
, dictObjPtr
, Jim_NewIntObj(interp
, value
));
1275 Jim_SetResult(interp
, dictObjPtr
);
1283 for (i
= 0; i
< sizeof(sockopts
) / sizeof(*sockopts
); i
++) {
1284 if (strcmp(Jim_String(argv
[0]), sockopts
[i
].name
) == 0) {
1286 if (sockopts
[i
].type
== SOCKOPT_BOOL
) {
1287 if (Jim_GetBoolean(interp
, argv
[1], &on
) != JIM_OK
) {
1293 if (Jim_GetLong(interp
, argv
[1], &longval
) != JIM_OK
) {
1298 if (setsockopt(af
->fd
, sockopts
[i
].level
, sockopts
[i
].opt
, (void *)&on
, sizeof(on
)) < 0) {
1299 Jim_SetResultFormatted(interp
, "Failed to set %#s: %s", argv
[0], strerror(errno
));
1306 Jim_SetResultFormatted(interp
, "Unknown sockopt %#s", argv
[0]);
1309 #endif /* JIM_BOOTSTRAP */
1312 static int aio_cmd_sync(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1314 AioFile
*af
= Jim_CmdPrivData(interp
);
1322 static int aio_cmd_buffering(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1324 AioFile
*af
= Jim_CmdPrivData(interp
);
1326 static const char * const options
[] = {
1340 if (Jim_GetEnum(interp
, argv
[0], options
, &option
, NULL
, JIM_ERRMSG
) != JIM_OK
) {
1345 setvbuf(af
->fp
, NULL
, _IONBF
, 0);
1348 setvbuf(af
->fp
, NULL
, _IOLBF
, BUFSIZ
);
1351 setvbuf(af
->fp
, NULL
, _IOFBF
, BUFSIZ
);
1357 #ifdef jim_ext_eventloop
1358 static void JimAioFileEventFinalizer(Jim_Interp
*interp
, void *clientData
)
1360 Jim_Obj
**objPtrPtr
= clientData
;
1362 Jim_DecrRefCount(interp
, *objPtrPtr
);
1366 static int JimAioFileEventHandler(Jim_Interp
*interp
, void *clientData
, int mask
)
1368 Jim_Obj
**objPtrPtr
= clientData
;
1370 return Jim_EvalObjBackground(interp
, *objPtrPtr
);
1373 static int aio_eventinfo(Jim_Interp
*interp
, AioFile
* af
, unsigned mask
, Jim_Obj
**scriptHandlerObj
,
1374 int argc
, Jim_Obj
* const *argv
)
1377 /* Return current script */
1378 if (*scriptHandlerObj
) {
1379 Jim_SetResult(interp
, *scriptHandlerObj
);
1384 if (*scriptHandlerObj
) {
1385 /* Delete old handler */
1386 Jim_DeleteFileHandler(interp
, af
->fd
, mask
);
1389 /* Now possibly add the new script(s) */
1390 if (Jim_Length(argv
[0]) == 0) {
1391 /* Empty script, so done */
1395 /* A new script to add */
1396 Jim_IncrRefCount(argv
[0]);
1397 *scriptHandlerObj
= argv
[0];
1399 Jim_CreateFileHandler(interp
, af
->fd
, mask
,
1400 JimAioFileEventHandler
, scriptHandlerObj
, JimAioFileEventFinalizer
);
1405 static int aio_cmd_readable(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1407 AioFile
*af
= Jim_CmdPrivData(interp
);
1409 return aio_eventinfo(interp
, af
, JIM_EVENT_READABLE
, &af
->rEvent
, argc
, argv
);
1412 static int aio_cmd_writable(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1414 AioFile
*af
= Jim_CmdPrivData(interp
);
1416 return aio_eventinfo(interp
, af
, JIM_EVENT_WRITABLE
, &af
->wEvent
, argc
, argv
);
1419 static int aio_cmd_onexception(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1421 AioFile
*af
= Jim_CmdPrivData(interp
);
1423 return aio_eventinfo(interp
, af
, JIM_EVENT_EXCEPTION
, &af
->eEvent
, argc
, argv
);
1427 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1428 static int aio_cmd_ssl(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1430 AioFile
*af
= Jim_CmdPrivData(interp
);
1434 const char *sni
= NULL
;
1437 static const char * const options
[] = { "-server", "-sni", NULL
};
1438 enum { OPT_SERVER
, OPT_SNI
};
1441 if (Jim_GetEnum(interp
, argv
[2], options
, &option
, NULL
, JIM_ERRMSG
) != JIM_OK
) {
1446 if (argc
!= 4 && argc
!= 5) {
1456 sni
= Jim_String(argv
[3]);
1462 Jim_SetResultFormatted(interp
, "%#s: stream is already ssl", argv
[0]);
1466 ssl_ctx
= JimAioSslCtx(interp
);
1467 if (ssl_ctx
== NULL
) {
1471 ssl
= SSL_new(ssl_ctx
);
1476 SSL_set_cipher_list(ssl
, "ALL");
1478 if (SSL_set_fd(ssl
, fileno(af
->fp
)) == 0) {
1483 const char *certfile
= Jim_String(argv
[3]);
1484 const char *keyfile
= (argc
== 4) ? certfile
: Jim_String(argv
[4]);
1485 if (SSL_use_certificate_file(ssl
, certfile
, SSL_FILETYPE_PEM
) != 1) {
1488 if (SSL_use_PrivateKey_file(ssl
, keyfile
, SSL_FILETYPE_PEM
) != 1) {
1492 if (SSL_accept(ssl
) != 1) {
1498 /* Set server name indication if requested */
1499 SSL_set_tlsext_host_name(ssl
, sni
);
1501 if (SSL_connect(ssl
) != 1) {
1507 af
->fops
= &ssl_fops
;
1509 /* Set the command name as the result */
1510 Jim_SetResult(interp
, argv
[0]);
1518 Jim_SetResultString(interp
, ERR_error_string(ERR_get_error(), NULL
), -1);
1522 static int aio_cmd_verify(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1524 AioFile
*af
= Jim_CmdPrivData(interp
);
1527 if (!af
->fops
->verify
) {
1531 ret
= af
->fops
->verify(af
);
1532 if (ret
!= JIM_OK
) {
1533 if (JimCheckStreamError(interp
, af
) == JIM_OK
) {
1534 Jim_SetResultString(interp
, "failed to verify the connection authenticity", -1);
1539 #endif /* JIM_BOOTSTRAP */
1541 #if defined(HAVE_STRUCT_FLOCK) && !defined(JIM_BOOTSTRAP)
1542 static int aio_cmd_lock(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1544 AioFile
*af
= Jim_CmdPrivData(interp
);
1546 int lockmode
= F_SETLK
;
1549 if (!Jim_CompareStringImmediate(interp
, argv
[0], "-wait")) {
1552 lockmode
= F_SETLKW
;
1557 fl
.l_type
= F_WRLCK
;
1558 fl
.l_whence
= SEEK_SET
;
1560 switch (fcntl(af
->fd
, lockmode
, &fl
))
1563 Jim_SetResultInt(interp
, 1);
1566 if (errno
== EACCES
|| errno
== EAGAIN
)
1567 Jim_SetResultInt(interp
, 0);
1570 Jim_SetResultFormatted(interp
, "lock failed: %s",
1576 Jim_SetResultInt(interp
, 0);
1583 static int aio_cmd_unlock(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1585 AioFile
*af
= Jim_CmdPrivData(interp
);
1589 fl
.l_type
= F_UNLCK
;
1590 fl
.l_whence
= SEEK_SET
;
1592 Jim_SetResultInt(interp
, fcntl(af
->fd
, F_SETLK
, &fl
) == 0);
1595 #endif /* JIM_BOOTSTRAP */
1597 #if defined(HAVE_TERMIOS_H) && !defined(JIM_BOOTSTRAP)
1598 static int aio_cmd_tty(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1600 AioFile
*af
= Jim_CmdPrivData(interp
);
1601 Jim_Obj
*dictObjPtr
;
1605 /* get the current settings as a dictionary */
1606 dictObjPtr
= Jim_GetTtySettings(interp
, af
->fd
);
1607 if (dictObjPtr
== NULL
) {
1608 JimAioSetError(interp
, NULL
);
1611 Jim_SetResult(interp
, dictObjPtr
);
1616 /* Convert name value arguments to a dictionary */
1617 dictObjPtr
= Jim_NewListObj(interp
, argv
, argc
);
1620 /* The settings are already given as a list */
1621 dictObjPtr
= argv
[0];
1623 Jim_IncrRefCount(dictObjPtr
);
1625 if (Jim_ListLength(interp
, dictObjPtr
) % 2) {
1626 /* Must be a valid dictionary */
1627 Jim_DecrRefCount(interp
, dictObjPtr
);
1631 ret
= Jim_SetTtySettings(interp
, af
->fd
, dictObjPtr
);
1633 JimAioSetError(interp
, NULL
);
1636 Jim_DecrRefCount(interp
, dictObjPtr
);
1640 #endif /* JIM_BOOTSTRAP */
1642 static const jim_subcmd_type aio_command_table
[] = {
1644 "?-nonewline|-pending|len?",
1648 /* Description: Read and return bytes from the stream. To eof if no len. */
1655 /* Description: Copy up to 'size' bytes to the given filehandle, or to eof if no size. */
1662 /* Description: Internal command to return the underlying file descriptor. */
1669 /* Description: Read one line and return it or store it in the var */
1676 /* Description: Write the string, with newline unless -nonewline */
1683 /* Description: Is the file descriptor a tty? */
1685 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
1691 /* Description: Receive up to 'len' bytes on the socket. Sets 'addrvar' with receive address, if set */
1698 /* Description: Send 'str' to the given address (dgram only) */
1705 /* Description: Server socket only: Accept a connection and return stream */
1712 /* Description: Set the listen backlog for server socket */
1719 /* Description: Return a dictionary of sockopts, or set the value of a sockopt */
1726 /* Description: Returns the local address of the socket, if any */
1733 /* Description: Returns the remote address of the socket, if any */
1735 #endif /* JIM_BOOTSTRAP */
1741 /* Description: Flush the stream */
1748 /* Description: Returns 1 if stream is at eof */
1755 JIM_MODFLAG_FULLARGV
,
1756 /* Description: Closes the stream. */
1759 "offset ?start|current|end",
1763 /* Description: Seeks in the stream (default 'current') */
1770 /* Description: Returns the current seek position */
1777 /* Description: Returns the original filename */
1785 /* Description: Set O_NDELAY (if arg). Returns current/new setting. */
1794 /* Description: Flush and fsync() the stream */
1802 /* Description: Sets buffering */
1804 #ifdef jim_ext_eventloop
1806 "?readable-script?",
1810 /* Description: Returns script, or invoke readable-script when readable, {} to remove */
1813 "?writable-script?",
1817 /* Description: Returns script, or invoke writable-script when writable, {} to remove */
1820 "?exception-script?",
1821 aio_cmd_onexception
,
1824 /* Description: Returns script, or invoke exception-script when oob data, {} to remove */
1827 #if !defined(JIM_BOOTSTRAP)
1828 #if defined(JIM_SSL)
1830 "?-server cert ?priv?|-sni servername?",
1834 JIM_MODFLAG_FULLARGV
1835 /* Description: Wraps a stream socket with SSL/TLS and returns a new channel */
1842 /* Description: Verifies the certificate of a SSL/TLS channel */
1845 #if defined(HAVE_STRUCT_FLOCK)
1851 /* Description: Attempt to get a lock, possibly waiting */
1858 /* Description: Relase a lock. */
1861 #if defined(HAVE_TERMIOS_H)
1863 "?baud rate? ?data bits? ?stop bits? ?parity even|odd|none? ?handshake xonxoff|rtscts|none? ?input raw|cooked? ?output raw|cooked? ?echo 0|1? ?vmin n? ?vtime n?",
1867 /* Description: Get or set tty settings - valid only on a tty */
1870 #endif /* JIM_BOOTSTRAP */
1874 static int JimAioSubCmdProc(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1876 return Jim_CallSubCmd(interp
, Jim_ParseSubCmd(interp
, aio_command_table
, argc
, argv
), argc
, argv
);
1879 static int JimAioOpenCommand(Jim_Interp
*interp
, int argc
,
1880 Jim_Obj
*const *argv
)
1884 if (argc
!= 2 && argc
!= 3) {
1885 Jim_WrongNumArgs(interp
, 1, argv
, "filename ?mode?");
1889 mode
= (argc
== 3) ? Jim_String(argv
[2]) : "r";
1891 #ifdef jim_ext_tclcompat
1893 const char *filename
= Jim_String(argv
[1]);
1895 /* If the filename starts with '|', use popen instead */
1896 if (*filename
== '|') {
1897 Jim_Obj
*evalObj
[3];
1899 evalObj
[0] = Jim_NewStringObj(interp
, "::popen", -1);
1900 evalObj
[1] = Jim_NewStringObj(interp
, filename
+ 1, -1);
1901 evalObj
[2] = Jim_NewStringObj(interp
, mode
, -1);
1903 return Jim_EvalObjVector(interp
, 3, evalObj
);
1907 return JimMakeChannel(interp
, NULL
, -1, argv
[1], "aio.handle%ld", 0, mode
, 0) ? JIM_OK
: JIM_ERR
;
1910 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1911 static void JimAioSslContextDelProc(struct Jim_Interp
*interp
, void *privData
)
1913 SSL_CTX_free((SSL_CTX
*)privData
);
1917 #ifdef USE_TLSv1_2_method
1918 #define TLS_method TLSv1_2_method
1921 static SSL_CTX
*JimAioSslCtx(Jim_Interp
*interp
)
1923 SSL_CTX
*ssl_ctx
= (SSL_CTX
*)Jim_GetAssocData(interp
, "ssl_ctx");
1924 if (ssl_ctx
== NULL
) {
1925 SSL_load_error_strings();
1927 ssl_ctx
= SSL_CTX_new(TLS_method());
1928 if (ssl_ctx
&& SSL_CTX_set_default_verify_paths(ssl_ctx
)) {
1929 SSL_CTX_set_verify(ssl_ctx
, SSL_VERIFY_NONE
, NULL
);
1930 Jim_SetAssocData(interp
, "ssl_ctx", JimAioSslContextDelProc
, ssl_ctx
);
1932 Jim_SetResultString(interp
, ERR_error_string(ERR_get_error(), NULL
), -1);
1937 #endif /* JIM_BOOTSTRAP */
1940 * Creates a channel for fh/fd/filename.
1942 * If fh is not NULL, uses that as the channel (and sets AIO_KEEPOPEN).
1943 * Otherwise, if fd is >= 0, uses that as the channel.
1944 * Otherwise opens 'filename' with mode 'mode'.
1946 * hdlfmt is a sprintf format for the filehandle. Anything with %ld at the end will do.
1947 * mode is used for open or fdopen.
1949 * Creates the command and sets the name as the current result.
1950 * Returns the AioFile pointer on sucess or NULL on failure.
1952 static AioFile
*JimMakeChannel(Jim_Interp
*interp
, FILE *fh
, int fd
, Jim_Obj
*filename
,
1953 const char *hdlfmt
, int family
, const char *mode
, int flags
)
1956 char buf
[AIO_CMD_LEN
];
1958 snprintf(buf
, sizeof(buf
), hdlfmt
, Jim_GetId(interp
));
1960 filename
= Jim_NewStringObj(interp
, buf
, -1);
1963 Jim_IncrRefCount(filename
);
1968 fh
= fdopen(fd
, mode
);
1972 fh
= fopen(Jim_String(filename
), mode
);
1975 JimAioSetError(interp
, filename
);
1981 Jim_DecrRefCount(interp
, filename
);
1986 /* Create the file command */
1987 af
= Jim_Alloc(sizeof(*af
));
1988 memset(af
, 0, sizeof(*af
));
1990 af
->filename
= filename
;
1993 af
->fd
= fileno(fh
);
1995 if ((flags
& AIO_KEEPOPEN
) == 0) {
1996 (void)fcntl(af
->fd
, F_SETFD
, FD_CLOEXEC
);
2000 af
->addr_family
= family
;
2001 af
->fops
= &stdio_fops
;
2004 Jim_CreateCommand(interp
, buf
, JimAioSubCmdProc
, af
, JimAioDelProc
);
2006 /* Note that the command must use the global namespace, even if
2007 * the current namespace is something different
2009 Jim_SetResult(interp
, Jim_MakeGlobalNamespaceName(interp
, Jim_NewStringObj(interp
, buf
, -1)));
2014 #if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && UNIX_SOCKETS) || defined(HAVE_OPENPTY)
2016 * Create a pair of channels. e.g. from pipe() or socketpair()
2018 static int JimMakeChannelPair(Jim_Interp
*interp
, int p
[2], Jim_Obj
*filename
,
2019 const char *hdlfmt
, int family
, const char * const mode
[2])
2021 if (JimMakeChannel(interp
, NULL
, p
[0], filename
, hdlfmt
, family
, mode
[0], 0)) {
2022 Jim_Obj
*objPtr
= Jim_NewListObj(interp
, NULL
, 0);
2023 Jim_ListAppendElement(interp
, objPtr
, Jim_GetResult(interp
));
2024 if (JimMakeChannel(interp
, NULL
, p
[1], filename
, hdlfmt
, family
, mode
[1], 0)) {
2025 Jim_ListAppendElement(interp
, objPtr
, Jim_GetResult(interp
));
2026 Jim_SetResult(interp
, objPtr
);
2031 /* Can only be here if fdopen() failed */
2034 JimAioSetError(interp
, NULL
);
2040 static int JimAioPipeCommand(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
2043 static const char * const mode
[2] = { "r", "w" };
2046 Jim_WrongNumArgs(interp
, 1, argv
, "");
2051 JimAioSetError(interp
, NULL
);
2055 return JimMakeChannelPair(interp
, p
, argv
[0], "aio.pipe%ld", 0, mode
);
2060 static int JimAioOpenPtyCommand(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
2063 static const char * const mode
[2] = { "r+", "w+" };
2066 Jim_WrongNumArgs(interp
, 1, argv
, "");
2070 if (openpty(&p
[0], &p
[1], NULL
, NULL
, NULL
) != 0) {
2071 JimAioSetError(interp
, NULL
);
2075 return JimMakeChannelPair(interp
, p
, argv
[0], "aio.pty%ld", 0, mode
);
2079 #if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
2081 static int JimAioSockCommand(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
2083 const char *socktypes
[] = {
2087 "unix.dgram.server",
2102 SOCK_UNIX_DGRAM_SERVER
,
2108 SOCK_STREAM_SOCKETPAIR
,
2113 const char *addr
= NULL
;
2114 const char *bind_addr
= NULL
;
2115 const char *connect_addr
= NULL
;
2116 union sockaddr_any sa
;
2121 int family
= PF_INET
;
2122 int type
= SOCK_STREAM
;
2123 Jim_Obj
*argv0
= argv
[0];
2126 if (argc
> 1 && Jim_CompareStringImmediate(interp
, argv
[1], "-ipv6")) {
2128 Jim_SetResultString(interp
, "ipv6 not supported", -1);
2139 Jim_WrongNumArgs(interp
, 1, &argv0
, "?-ipv6? type ?address?");
2143 if (Jim_GetEnum(interp
, argv
[1], socktypes
, &socktype
, "socket type", JIM_ERRMSG
) != JIM_OK
)
2144 return Jim_CheckShowCommands(interp
, argv
[1], socktypes
);
2146 Jim_SetEmptyResult(interp
);
2149 addr
= Jim_String(argv
[2]);
2152 #if defined(HAVE_SOCKETPAIR) && UNIX_SOCKETS
2153 if (socktype
== SOCK_STREAM_SOCKETPAIR
) {
2155 static const char * const mode
[2] = { "r+", "r+" };
2161 if (socketpair(PF_UNIX
, SOCK_STREAM
, 0, p
) < 0) {
2162 JimAioSetError(interp
, NULL
);
2165 return JimMakeChannelPair(interp
, p
, argv
[1], "aio.sockpair%ld", PF_UNIX
, mode
);
2169 #if defined(HAVE_PIPE)
2170 if (socktype
== SOCK_STREAM_PIPE
) {
2174 return JimAioPipeCommand(interp
, 1, &argv
[1]);
2178 /* Now all these socket types are very similar */
2180 case SOCK_DGRAM_CLIENT
:
2181 connect_addr
= addr
;
2185 case SOCK_STREAM_CLIENT
:
2189 connect_addr
= addr
;
2192 case SOCK_STREAM_SERVER
:
2201 case SOCK_DGRAM_SERVER
:
2215 connect_addr
= addr
;
2219 case SOCK_UNIX_DGRAM
:
2220 connect_addr
= addr
;
2223 /* A dgram unix domain socket client needs to bind
2224 * to a temporary address to allow the server to
2228 int tmpfd
= Jim_MakeTempFile(interp
, NULL
, 1);
2233 /* This will be valid until a result is next set, which is long enough here */
2234 bind_addr
= Jim_String(Jim_GetResult(interp
));
2238 case SOCK_UNIX_SERVER
:
2247 case SOCK_UNIX_DGRAM_SERVER
:
2257 case SOCK_STREAM_PTY
:
2261 return JimAioOpenPtyCommand(interp
, 1, &argv
[1]);
2265 Jim_SetResultString(interp
, "Unsupported socket type", -1);
2269 /* Now do all the steps necessary for the given socket type */
2270 sock
= socket(family
, type
, 0);
2272 JimAioSetError(interp
, NULL
);
2276 if (JimParseSocketAddress(interp
, family
, bind_addr
, &sa
, &salen
) != JIM_OK
) {
2281 setsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, (void *)&on
, sizeof(on
));
2283 if (bind(sock
, &sa
.sa
, salen
)) {
2284 Jim_SetResultFormatted(interp
, "%s: bind: %s", bind_addr
, strerror(errno
));
2290 if (JimParseSocketAddress(interp
, family
, connect_addr
, &sa
, &salen
) != JIM_OK
) {
2294 if (connect(sock
, &sa
.sa
, salen
)) {
2295 Jim_SetResultFormatted(interp
, "%s: connect: %s", connect_addr
, strerror(errno
));
2301 if (listen(sock
, 5)) {
2302 Jim_SetResultFormatted(interp
, "listen: %s", strerror(errno
));
2308 return JimMakeChannel(interp
, NULL
, sock
, argv
[1], "aio.sock%ld", family
, "r+", 0) ? JIM_OK
: JIM_ERR
;
2310 #endif /* JIM_BOOTSTRAP */
2312 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
2313 static int JimAioLoadSSLCertsCommand(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
2318 Jim_WrongNumArgs(interp
, 1, argv
, "dir");
2322 ssl_ctx
= JimAioSslCtx(interp
);
2326 if (SSL_CTX_load_verify_locations(ssl_ctx
, NULL
, Jim_String(argv
[1])) == 1) {
2329 Jim_SetResultString(interp
, ERR_error_string(ERR_get_error(), NULL
), -1);
2332 #endif /* JIM_BOOTSTRAP */
2334 int Jim_aioInit(Jim_Interp
*interp
)
2336 if (Jim_PackageProvide(interp
, "aio", "1.0", JIM_ERRMSG
))
2339 #if defined(JIM_SSL)
2340 Jim_CreateCommand(interp
, "load_ssl_certs", JimAioLoadSSLCertsCommand
, NULL
, NULL
);
2343 Jim_CreateCommand(interp
, "open", JimAioOpenCommand
, NULL
, NULL
);
2345 Jim_CreateCommand(interp
, "socket", JimAioSockCommand
, NULL
, NULL
);
2348 Jim_CreateCommand(interp
, "pipe", JimAioPipeCommand
, NULL
, NULL
);
2351 /* Create filehandles for stdin, stdout and stderr */
2352 JimMakeChannel(interp
, stdin
, -1, NULL
, "stdin", 0, "r", AIO_KEEPOPEN
);
2353 JimMakeChannel(interp
, stdout
, -1, NULL
, "stdout", 0, "w", AIO_KEEPOPEN
);
2354 JimMakeChannel(interp
, stderr
, -1, NULL
, "stderr", 0, "w", AIO_KEEPOPEN
);