lock.test: Improve check for aio.lock support
[jimtcl.git] / jim-aio.c
blobfb8577f08255a654ff30a366bfefea0ff0a78dc4
1 /* Jim - A small embeddable Tcl interpreter
3 * Copyright 2005 Salvatore Sanfilippo <antirez@invece.org>
4 * Copyright 2005 Clemens Hintze <c.hintze@gmx.net>
5 * Copyright 2005 patthoyts - Pat Thoyts <patthoyts@users.sf.net>
6 * Copyright 2008 oharboe - Øyvind Harboe - oyvind.harboe@zylin.com
7 * Copyright 2008 Andrew Lunn <andrew@lunn.ch>
8 * Copyright 2008 Duane Ellis <openocd@duaneellis.com>
9 * Copyright 2008 Uwe Klein <uklein@klein-messgeraete.de>
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above
18 * copyright notice, this list of conditions and the following
19 * disclaimer in the documentation and/or other materials
20 * provided with the distribution.
22 * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY
23 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
24 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
25 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
33 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 * The views and conclusions contained in the software and documentation
36 * are those of the authors and should not be interpreted as representing
37 * official policies, either expressed or implied, of the Jim Tcl Project.
38 **/
40 #include "jimautoconf.h"
42 #define _GNU_SOURCE
43 #include <stdio.h>
44 #include <string.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #ifdef HAVE_UNISTD_H
48 #include <unistd.h>
49 #include <sys/stat.h>
50 #endif
52 #include "jim.h"
54 #if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H)
55 #include <sys/socket.h>
56 #include <netinet/in.h>
57 #include <arpa/inet.h>
58 #include <netdb.h>
59 #ifdef HAVE_SYS_UN_H
60 #include <sys/un.h>
61 #endif
62 #else
63 #define JIM_ANSIC
64 #endif
66 #if defined(JIM_SSL)
67 #include <openssl/ssl.h>
68 #include <openssl/err.h>
69 #endif
71 #include "jim-eventloop.h"
72 #include "jim-subcmd.h"
74 #define AIO_CMD_LEN 32 /* e.g. aio.handleXXXXXX */
75 #define AIO_BUF_LEN 256 /* Can keep this small and rely on stdio buffering */
77 #ifndef HAVE_FTELLO
78 #define ftello ftell
79 #endif
80 #ifndef HAVE_FSEEKO
81 #define fseeko fseek
82 #endif
84 #define AIO_KEEPOPEN 1
86 #if defined(JIM_IPV6)
87 #define IPV6 1
88 #else
89 #define IPV6 0
90 #ifndef PF_INET6
91 #define PF_INET6 0
92 #endif
93 #endif
95 #define JimCheckStreamError(interp, af) af->fops->error(af)
97 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
98 union sockaddr_any {
99 struct sockaddr sa;
100 struct sockaddr_in sin;
101 #if IPV6
102 struct sockaddr_in6 sin6;
103 #endif
106 #ifndef HAVE_INET_NTOP
107 const char *inet_ntop(int af, const void *src, char *dst, int size)
109 if (af != PF_INET) {
110 return NULL;
112 snprintf(dst, size, "%s", inet_ntoa(((struct sockaddr_in *)src)->sin_addr));
113 return dst;
115 #endif
116 #endif /* JIM_BOOTSTRAP */
118 struct AioFile;
120 typedef struct {
121 int (*writer)(struct AioFile *af, const char *buf, int len);
122 int (*reader)(struct AioFile *af, char *buf, int len);
123 const char *(*getline)(struct AioFile *af, char *buf, int len);
124 int (*error)(const struct AioFile *af);
125 const char *(*strerror)(struct AioFile *af);
126 int (*verify)(struct AioFile *af);
127 } JimAioFopsType;
129 typedef struct AioFile
131 FILE *fp;
132 Jim_Obj *filename;
133 int type;
134 int openFlags; /* AIO_KEEPOPEN? keep FILE* */
135 int fd;
136 Jim_Obj *rEvent;
137 Jim_Obj *wEvent;
138 Jim_Obj *eEvent;
139 int addr_family;
140 void *ssl;
141 const JimAioFopsType *fops;
142 } AioFile;
144 static int stdio_writer(struct AioFile *af, const char *buf, int len)
146 return fwrite(buf, 1, len, af->fp);
149 static int stdio_reader(struct AioFile *af, char *buf, int len)
151 return fread(buf, 1, len, af->fp);
154 static const char *stdio_getline(struct AioFile *af, char *buf, int len)
156 return fgets(buf, len, af->fp);
159 static int stdio_error(const AioFile *af)
161 if (!ferror(af->fp)) {
162 return JIM_OK;
164 clearerr(af->fp);
165 /* EAGAIN and similar are not error conditions. Just treat them like eof */
166 if (feof(af->fp) || errno == EAGAIN || errno == EINTR) {
167 return JIM_OK;
169 #ifdef ECONNRESET
170 if (errno == ECONNRESET) {
171 return JIM_OK;
173 #endif
174 #ifdef ECONNABORTED
175 if (errno != ECONNABORTED) {
176 return JIM_OK;
178 #endif
179 return JIM_ERR;
182 static const char *stdio_strerror(struct AioFile *af)
184 return strerror(errno);
187 static const JimAioFopsType stdio_fops = {
188 stdio_writer,
189 stdio_reader,
190 stdio_getline,
191 stdio_error,
192 stdio_strerror,
193 NULL
196 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
198 static SSL_CTX *JimAioSslCtx(Jim_Interp *interp);
200 static int ssl_writer(struct AioFile *af, const char *buf, int len)
202 return SSL_write(af->ssl, buf, len);
205 static int ssl_reader(struct AioFile *af, char *buf, int len)
207 return SSL_read(af->ssl, buf, len);
210 static const char *ssl_getline(struct AioFile *af, char *buf, int len)
212 int i;
213 for (i = 0; i < len + 1; i++) {
214 if (SSL_read(af->ssl, &buf[i], 1) != 1) {
215 if (i == 0) {
216 return NULL;
218 break;
220 if (buf[i] == '\n') {
221 break;
224 buf[i] = '\0';
225 return buf;
228 static int ssl_error(const struct AioFile *af)
230 if (ERR_peek_error() == 0) {
231 return JIM_OK;
234 return JIM_ERR;
237 static const char *ssl_strerror(struct AioFile *af)
239 int err = ERR_get_error();
241 if (err) {
242 return ERR_error_string(err, NULL);
245 /* should not happen */
246 return "unknown SSL error";
249 static int ssl_verify(struct AioFile *af)
251 X509 *cert;
253 cert = SSL_get_peer_certificate(af->ssl);
254 if (!cert) {
255 return JIM_ERR;
257 X509_free(cert);
259 if (SSL_get_verify_result(af->ssl) == X509_V_OK) {
260 return JIM_OK;
263 return JIM_ERR;
266 static const JimAioFopsType ssl_fops = {
267 ssl_writer,
268 ssl_reader,
269 ssl_getline,
270 ssl_error,
271 ssl_strerror,
272 ssl_verify
274 #endif /* JIM_BOOTSTRAP */
276 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
277 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
278 const char *hdlfmt, int family, const char *mode);
280 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
281 static int JimParseIPv6Address(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, int *salen)
283 #if IPV6
285 * An IPv6 addr/port looks like:
286 * [::1]
287 * [::1]:2000
288 * [fe80::223:6cff:fe95:bdc0%en1]:2000
289 * [::]:2000
290 * 2000
292 * Note that the "any" address is ::, which is the same as when no address is specified.
294 char *sthost = NULL;
295 const char *stport;
296 int ret = JIM_OK;
297 struct addrinfo req;
298 struct addrinfo *ai;
300 stport = strrchr(hostport, ':');
301 if (!stport) {
302 /* No : so, the whole thing is the port */
303 stport = hostport;
304 hostport = "::";
305 sthost = Jim_StrDup(hostport);
307 else {
308 stport++;
311 if (*hostport == '[') {
312 /* This is a numeric ipv6 address */
313 char *pt = strchr(++hostport, ']');
314 if (pt) {
315 sthost = Jim_StrDupLen(hostport, pt - hostport);
319 if (!sthost) {
320 sthost = Jim_StrDupLen(hostport, stport - hostport - 1);
323 memset(&req, '\0', sizeof(req));
324 req.ai_family = PF_INET6;
326 if (getaddrinfo(sthost, NULL, &req, &ai)) {
327 Jim_SetResultFormatted(interp, "Not a valid address: %s", hostport);
328 ret = JIM_ERR;
330 else {
331 memcpy(&sa->sin, ai->ai_addr, ai->ai_addrlen);
332 *salen = ai->ai_addrlen;
334 sa->sin.sin_port = htons(atoi(stport));
336 freeaddrinfo(ai);
338 Jim_Free(sthost);
340 return ret;
341 #else
342 Jim_SetResultString(interp, "ipv6 not supported", -1);
343 return JIM_ERR;
344 #endif
347 static int JimParseIpAddress(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, int *salen)
349 /* An IPv4 addr/port looks like:
350 * 192.168.1.5
351 * 192.168.1.5:2000
352 * 2000
354 * If the address is missing, INADDR_ANY is used.
355 * If the port is missing, 0 is used (only useful for server sockets).
357 char *sthost = NULL;
358 const char *stport;
359 int ret = JIM_OK;
361 stport = strrchr(hostport, ':');
362 if (!stport) {
363 /* No : so, the whole thing is the port */
364 stport = hostport;
365 sthost = Jim_StrDup("0.0.0.0");
367 else {
368 sthost = Jim_StrDupLen(hostport, stport - hostport);
369 stport++;
373 #ifdef HAVE_GETADDRINFO
374 struct addrinfo req;
375 struct addrinfo *ai;
376 memset(&req, '\0', sizeof(req));
377 req.ai_family = PF_INET;
379 if (getaddrinfo(sthost, NULL, &req, &ai)) {
380 ret = JIM_ERR;
382 else {
383 memcpy(&sa->sin, ai->ai_addr, ai->ai_addrlen);
384 *salen = ai->ai_addrlen;
385 freeaddrinfo(ai);
387 #else
388 struct hostent *he;
390 ret = JIM_ERR;
392 if ((he = gethostbyname(sthost)) != NULL) {
393 if (he->h_length == sizeof(sa->sin.sin_addr)) {
394 *salen = sizeof(sa->sin);
395 sa->sin.sin_family= he->h_addrtype;
396 memcpy(&sa->sin.sin_addr, he->h_addr, he->h_length); /* set address */
397 ret = JIM_OK;
400 #endif
402 sa->sin.sin_port = htons(atoi(stport));
404 Jim_Free(sthost);
406 if (ret != JIM_OK) {
407 Jim_SetResultFormatted(interp, "Not a valid address: %s", hostport);
410 return ret;
413 #ifdef HAVE_SYS_UN_H
414 static int JimParseDomainAddress(Jim_Interp *interp, const char *path, struct sockaddr_un *sa)
416 sa->sun_family = PF_UNIX;
417 snprintf(sa->sun_path, sizeof(sa->sun_path), "%s", path);
419 return JIM_OK;
421 #endif
424 * Format that address in 'sa' as a string and store in variable 'varObjPtr'
426 static int JimFormatIpAddress(Jim_Interp *interp, Jim_Obj *varObjPtr, const union sockaddr_any *sa)
428 /* INET6_ADDRSTRLEN is 46. Add some for [] and port */
429 char addrbuf[60];
431 #if IPV6
432 if (sa->sa.sa_family == PF_INET6) {
433 addrbuf[0] = '[';
434 /* Allow 9 for []:65535\0 */
435 inet_ntop(sa->sa.sa_family, &sa->sin6.sin6_addr, addrbuf + 1, sizeof(addrbuf) - 9);
436 snprintf(addrbuf + strlen(addrbuf), 8, "]:%d", ntohs(sa->sin.sin_port));
438 else
439 #endif
440 if (sa->sa.sa_family == PF_INET) {
441 /* Allow 7 for :65535\0 */
442 inet_ntop(sa->sa.sa_family, &sa->sin.sin_addr, addrbuf, sizeof(addrbuf) - 7);
443 snprintf(addrbuf + strlen(addrbuf), 7, ":%d", ntohs(sa->sin.sin_port));
445 else {
446 /* recvfrom still works on unix domain sockets, etc */
447 addrbuf[0] = 0;
450 return Jim_SetVariable(interp, varObjPtr, Jim_NewStringObj(interp, addrbuf, -1));
453 #endif /* JIM_BOOTSTRAP */
455 static const char *JimAioErrorString(AioFile *af)
457 if (af && af->fops)
458 return af->fops->strerror(af);
460 return strerror(errno);
463 static void JimAioSetError(Jim_Interp *interp, Jim_Obj *name)
465 AioFile *af = Jim_CmdPrivData(interp);
467 if (name) {
468 Jim_SetResultFormatted(interp, "%#s: %s", name, JimAioErrorString(af));
470 else {
471 Jim_SetResultString(interp, JimAioErrorString(af), -1);
475 static void JimAioDelProc(Jim_Interp *interp, void *privData)
477 AioFile *af = privData;
479 JIM_NOTUSED(interp);
481 Jim_DecrRefCount(interp, af->filename);
483 #ifdef jim_ext_eventloop
484 /* remove all existing EventHandlers */
485 Jim_DeleteFileHandler(interp, af->fd, JIM_EVENT_READABLE | JIM_EVENT_WRITABLE | JIM_EVENT_EXCEPTION);
486 #endif
488 #if defined(JIM_SSL)
489 if (af->ssl != NULL) {
490 SSL_free(af->ssl);
492 #endif
494 if (!(af->openFlags & AIO_KEEPOPEN)) {
495 fclose(af->fp);
498 Jim_Free(af);
501 static int aio_cmd_read(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
503 AioFile *af = Jim_CmdPrivData(interp);
504 char buf[AIO_BUF_LEN];
505 Jim_Obj *objPtr;
506 int nonewline = 0;
507 jim_wide neededLen = -1; /* -1 is "read as much as possible" */
509 if (argc && Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
510 nonewline = 1;
511 argv++;
512 argc--;
514 if (argc == 1) {
515 if (Jim_GetWide(interp, argv[0], &neededLen) != JIM_OK)
516 return JIM_ERR;
517 if (neededLen < 0) {
518 Jim_SetResultString(interp, "invalid parameter: negative len", -1);
519 return JIM_ERR;
522 else if (argc) {
523 return -1;
525 objPtr = Jim_NewStringObj(interp, NULL, 0);
526 while (neededLen != 0) {
527 int retval;
528 int readlen;
530 if (neededLen == -1) {
531 readlen = AIO_BUF_LEN;
533 else {
534 readlen = (neededLen > AIO_BUF_LEN ? AIO_BUF_LEN : neededLen);
536 retval = af->fops->reader(af, buf, readlen);
537 if (retval > 0) {
538 Jim_AppendString(interp, objPtr, buf, retval);
539 if (neededLen != -1) {
540 neededLen -= retval;
543 if (retval != readlen)
544 break;
546 /* Check for error conditions */
547 if (JimCheckStreamError(interp, af)) {
548 Jim_FreeNewObj(interp, objPtr);
549 return JIM_ERR;
551 if (nonewline) {
552 int len;
553 const char *s = Jim_GetString(objPtr, &len);
555 if (len > 0 && s[len - 1] == '\n') {
556 objPtr->length--;
557 objPtr->bytes[objPtr->length] = '\0';
560 Jim_SetResult(interp, objPtr);
561 return JIM_OK;
564 AioFile *Jim_AioFile(Jim_Interp *interp, Jim_Obj *command)
566 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, command, JIM_ERRMSG);
568 /* XXX: There ought to be a supported API for this */
569 if (cmdPtr && !cmdPtr->isproc && cmdPtr->u.native.cmdProc == JimAioSubCmdProc) {
570 return (AioFile *) cmdPtr->u.native.privData;
572 Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", command);
573 return NULL;
576 FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command)
578 AioFile *af;
580 af = Jim_AioFile(interp, command);
581 if (af == NULL) {
582 return NULL;
585 return af->fp;
588 static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
590 AioFile *af = Jim_CmdPrivData(interp);
591 jim_wide count = 0;
592 jim_wide maxlen = JIM_WIDE_MAX;
593 AioFile *outf = Jim_AioFile(interp, argv[0]);
595 if (outf == NULL) {
596 return JIM_ERR;
599 if (argc == 2) {
600 if (Jim_GetWide(interp, argv[1], &maxlen) != JIM_OK) {
601 return JIM_ERR;
605 while (count < maxlen) {
606 char ch;
608 if (af->fops->reader(af, &ch, 1) != 1) {
609 break;
611 if (outf->fops->writer(outf, &ch, 1) != 1) {
612 break;
614 count++;
617 if (JimCheckStreamError(interp, af) || JimCheckStreamError(interp, outf)) {
618 return JIM_ERR;
621 Jim_SetResultInt(interp, count);
623 return JIM_OK;
626 static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
628 AioFile *af = Jim_CmdPrivData(interp);
629 char buf[AIO_BUF_LEN];
630 Jim_Obj *objPtr;
631 int len;
633 errno = 0;
635 objPtr = Jim_NewStringObj(interp, NULL, 0);
636 while (1) {
637 buf[AIO_BUF_LEN - 1] = '_';
639 if (af->fops->getline(af, buf, AIO_BUF_LEN) == NULL)
640 break;
642 if (buf[AIO_BUF_LEN - 1] == '\0' && buf[AIO_BUF_LEN - 2] != '\n') {
643 Jim_AppendString(interp, objPtr, buf, AIO_BUF_LEN - 1);
645 else {
646 len = strlen(buf);
648 if (len && (buf[len - 1] == '\n')) {
649 /* strip "\n" */
650 len--;
653 Jim_AppendString(interp, objPtr, buf, len);
654 break;
658 if (JimCheckStreamError(interp, af)) {
659 /* I/O error */
660 Jim_FreeNewObj(interp, objPtr);
661 return JIM_ERR;
664 if (argc) {
665 if (Jim_SetVariable(interp, argv[0], objPtr) != JIM_OK) {
666 Jim_FreeNewObj(interp, objPtr);
667 return JIM_ERR;
670 len = Jim_Length(objPtr);
672 if (len == 0 && feof(af->fp)) {
673 /* On EOF returns -1 if varName was specified */
674 len = -1;
676 Jim_SetResultInt(interp, len);
678 else {
679 Jim_SetResult(interp, objPtr);
681 return JIM_OK;
684 static int aio_cmd_puts(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
686 AioFile *af = Jim_CmdPrivData(interp);
687 int wlen;
688 const char *wdata;
689 Jim_Obj *strObj;
691 if (argc == 2) {
692 if (!Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
693 return -1;
695 strObj = argv[1];
697 else {
698 strObj = argv[0];
701 wdata = Jim_GetString(strObj, &wlen);
702 if (af->fops->writer(af, wdata, wlen) == wlen) {
703 if (argc == 2 || af->fops->writer(af, "\n", 1) == 1) {
704 return JIM_OK;
707 JimAioSetError(interp, af->filename);
708 return JIM_ERR;
711 static int aio_cmd_isatty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
713 #ifdef HAVE_ISATTY
714 AioFile *af = Jim_CmdPrivData(interp);
715 Jim_SetResultInt(interp, isatty(fileno(af->fp)));
716 #else
717 Jim_SetResultInt(interp, 0);
718 #endif
720 return JIM_OK;
723 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
724 static int aio_cmd_recvfrom(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
726 AioFile *af = Jim_CmdPrivData(interp);
727 char *buf;
728 union sockaddr_any sa;
729 long len;
730 socklen_t salen = sizeof(sa);
731 int rlen;
733 if (Jim_GetLong(interp, argv[0], &len) != JIM_OK) {
734 return JIM_ERR;
737 buf = Jim_Alloc(len + 1);
739 rlen = recvfrom(fileno(af->fp), buf, len, 0, &sa.sa, &salen);
740 if (rlen < 0) {
741 Jim_Free(buf);
742 JimAioSetError(interp, NULL);
743 return JIM_ERR;
745 buf[rlen] = 0;
746 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, rlen));
748 if (argc > 1) {
749 return JimFormatIpAddress(interp, argv[1], &sa);
752 return JIM_OK;
756 static int aio_cmd_sendto(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
758 AioFile *af = Jim_CmdPrivData(interp);
759 int wlen;
760 int len;
761 const char *wdata;
762 union sockaddr_any sa;
763 const char *addr = Jim_String(argv[1]);
764 int salen;
766 if (IPV6 && af->addr_family == PF_INET6) {
767 if (JimParseIPv6Address(interp, addr, &sa, &salen) != JIM_OK) {
768 return JIM_ERR;
771 else if (JimParseIpAddress(interp, addr, &sa, &salen) != JIM_OK) {
772 return JIM_ERR;
774 wdata = Jim_GetString(argv[0], &wlen);
776 /* Note that we don't validate the socket type. Rely on sendto() failing if appropriate */
777 len = sendto(fileno(af->fp), wdata, wlen, 0, &sa.sa, salen);
778 if (len < 0) {
779 JimAioSetError(interp, NULL);
780 return JIM_ERR;
782 Jim_SetResultInt(interp, len);
783 return JIM_OK;
786 static int aio_cmd_accept(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
788 AioFile *af = Jim_CmdPrivData(interp);
789 int sock;
790 union sockaddr_any sa;
791 socklen_t addrlen = sizeof(sa);
793 sock = accept(af->fd, &sa.sa, &addrlen);
794 if (sock < 0) {
795 JimAioSetError(interp, NULL);
796 return JIM_ERR;
799 if (argc > 0) {
800 if (JimFormatIpAddress(interp, argv[0], &sa) != JIM_OK) {
801 return JIM_ERR;
805 /* Create the file command */
806 return JimMakeChannel(interp, NULL, sock, Jim_NewStringObj(interp, "accept", -1),
807 "aio.sockstream%ld", af->addr_family, "r+") ? JIM_OK : JIM_ERR;
810 static int aio_cmd_listen(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
812 AioFile *af = Jim_CmdPrivData(interp);
813 long backlog;
815 if (Jim_GetLong(interp, argv[0], &backlog) != JIM_OK) {
816 return JIM_ERR;
819 if (listen(af->fd, backlog)) {
820 JimAioSetError(interp, NULL);
821 return JIM_ERR;
824 return JIM_OK;
826 #endif /* JIM_BOOTSTRAP */
828 static int aio_cmd_flush(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
830 AioFile *af = Jim_CmdPrivData(interp);
832 if (fflush(af->fp) == EOF) {
833 JimAioSetError(interp, af->filename);
834 return JIM_ERR;
836 return JIM_OK;
839 static int aio_cmd_eof(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
841 AioFile *af = Jim_CmdPrivData(interp);
843 Jim_SetResultInt(interp, feof(af->fp));
844 return JIM_OK;
847 static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
849 if (argc == 3) {
850 #if !defined(JIM_ANSIC) && defined(HAVE_SHUTDOWN)
851 static const char * const options[] = { "r", "w", NULL };
852 enum { OPT_R, OPT_W, };
853 int option;
854 AioFile *af = Jim_CmdPrivData(interp);
856 if (Jim_GetEnum(interp, argv[2], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
857 return JIM_ERR;
859 if (shutdown(af->fd, option == OPT_R ? SHUT_RD : SHUT_WR) == 0) {
860 return JIM_OK;
862 JimAioSetError(interp, NULL);
863 #else
864 Jim_SetResultString(interp, "async close not supported", -1);
865 #endif
866 return JIM_ERR;
869 return Jim_DeleteCommand(interp, Jim_String(argv[0]));
872 static int aio_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
874 AioFile *af = Jim_CmdPrivData(interp);
875 int orig = SEEK_SET;
876 jim_wide offset;
878 if (argc == 2) {
879 if (Jim_CompareStringImmediate(interp, argv[1], "start"))
880 orig = SEEK_SET;
881 else if (Jim_CompareStringImmediate(interp, argv[1], "current"))
882 orig = SEEK_CUR;
883 else if (Jim_CompareStringImmediate(interp, argv[1], "end"))
884 orig = SEEK_END;
885 else {
886 return -1;
889 if (Jim_GetWide(interp, argv[0], &offset) != JIM_OK) {
890 return JIM_ERR;
892 if (fseeko(af->fp, offset, orig) == -1) {
893 JimAioSetError(interp, af->filename);
894 return JIM_ERR;
896 return JIM_OK;
899 static int aio_cmd_tell(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
901 AioFile *af = Jim_CmdPrivData(interp);
903 Jim_SetResultInt(interp, ftello(af->fp));
904 return JIM_OK;
907 static int aio_cmd_filename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
909 AioFile *af = Jim_CmdPrivData(interp);
911 Jim_SetResult(interp, af->filename);
912 return JIM_OK;
915 #ifdef O_NDELAY
916 static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
918 AioFile *af = Jim_CmdPrivData(interp);
920 int fmode = fcntl(af->fd, F_GETFL);
922 if (argc) {
923 long nb;
925 if (Jim_GetLong(interp, argv[0], &nb) != JIM_OK) {
926 return JIM_ERR;
928 if (nb) {
929 fmode |= O_NDELAY;
931 else {
932 fmode &= ~O_NDELAY;
934 (void)fcntl(af->fd, F_SETFL, fmode);
936 Jim_SetResultInt(interp, (fmode & O_NONBLOCK) ? 1 : 0);
937 return JIM_OK;
939 #endif
941 #ifdef HAVE_FSYNC
942 static int aio_cmd_sync(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
944 AioFile *af = Jim_CmdPrivData(interp);
946 fflush(af->fp);
947 fsync(af->fd);
948 return JIM_OK;
950 #endif
952 static int aio_cmd_buffering(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
954 AioFile *af = Jim_CmdPrivData(interp);
956 static const char * const options[] = {
957 "none",
958 "line",
959 "full",
960 NULL
962 enum
964 OPT_NONE,
965 OPT_LINE,
966 OPT_FULL,
968 int option;
970 if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
971 return JIM_ERR;
973 switch (option) {
974 case OPT_NONE:
975 setvbuf(af->fp, NULL, _IONBF, 0);
976 break;
977 case OPT_LINE:
978 setvbuf(af->fp, NULL, _IOLBF, BUFSIZ);
979 break;
980 case OPT_FULL:
981 setvbuf(af->fp, NULL, _IOFBF, BUFSIZ);
982 break;
984 return JIM_OK;
987 #ifdef jim_ext_eventloop
988 static void JimAioFileEventFinalizer(Jim_Interp *interp, void *clientData)
990 Jim_Obj **objPtrPtr = clientData;
992 Jim_DecrRefCount(interp, *objPtrPtr);
993 *objPtrPtr = NULL;
996 static int JimAioFileEventHandler(Jim_Interp *interp, void *clientData, int mask)
998 Jim_Obj **objPtrPtr = clientData;
1000 return Jim_EvalObjBackground(interp, *objPtrPtr);
1003 static int aio_eventinfo(Jim_Interp *interp, AioFile * af, unsigned mask, Jim_Obj **scriptHandlerObj,
1004 int argc, Jim_Obj * const *argv)
1006 if (argc == 0) {
1007 /* Return current script */
1008 if (*scriptHandlerObj) {
1009 Jim_SetResult(interp, *scriptHandlerObj);
1011 return JIM_OK;
1014 if (*scriptHandlerObj) {
1015 /* Delete old handler */
1016 Jim_DeleteFileHandler(interp, af->fd, mask);
1019 /* Now possibly add the new script(s) */
1020 if (Jim_Length(argv[0]) == 0) {
1021 /* Empty script, so done */
1022 return JIM_OK;
1025 /* A new script to add */
1026 Jim_IncrRefCount(argv[0]);
1027 *scriptHandlerObj = argv[0];
1029 Jim_CreateFileHandler(interp, af->fd, mask,
1030 JimAioFileEventHandler, scriptHandlerObj, JimAioFileEventFinalizer);
1032 return JIM_OK;
1035 static int aio_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1037 AioFile *af = Jim_CmdPrivData(interp);
1039 return aio_eventinfo(interp, af, JIM_EVENT_READABLE, &af->rEvent, argc, argv);
1042 static int aio_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1044 AioFile *af = Jim_CmdPrivData(interp);
1046 return aio_eventinfo(interp, af, JIM_EVENT_WRITABLE, &af->wEvent, argc, argv);
1049 static int aio_cmd_onexception(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1051 AioFile *af = Jim_CmdPrivData(interp);
1053 return aio_eventinfo(interp, af, JIM_EVENT_EXCEPTION, &af->eEvent, argc, argv);
1055 #endif
1057 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1058 static int aio_cmd_ssl(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1060 AioFile *af = Jim_CmdPrivData(interp);
1061 SSL *ssl;
1062 SSL_CTX *ssl_ctx;
1063 int fd, server = 0;
1065 if (argc == 5) {
1066 if (!Jim_CompareStringImmediate(interp, argv[2], "-server")) {
1067 return JIM_ERR;
1069 server = 1;
1071 else if (argc != 2) {
1072 Jim_WrongNumArgs(interp, 2, argv, "?-server cert priv?");
1073 return JIM_ERR;
1076 fd = fileno(af->fp);
1077 #if defined(HAVE_DUP)
1078 fd = dup(fd);
1079 if (fd < 0) {
1080 return JIM_ERR;
1082 #endif
1083 ssl_ctx = JimAioSslCtx(interp);
1084 if (ssl_ctx == NULL) {
1085 return JIM_ERR;
1088 ssl = SSL_new(ssl_ctx);
1089 if (ssl == NULL) {
1090 #if defined(HAVE_DUP)
1091 close(fd);
1092 #endif
1093 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1094 return JIM_ERR;
1097 SSL_set_cipher_list(ssl, "ALL");
1099 if (SSL_set_fd(ssl, fileno(af->fp)) == 0) {
1100 goto out;
1103 if (server) {
1104 if (SSL_use_certificate_file(ssl, Jim_String(argv[3]), SSL_FILETYPE_PEM) != 1) {
1105 goto out;
1108 if (SSL_use_PrivateKey_file(ssl, Jim_String(argv[4]), SSL_FILETYPE_PEM) != 1) {
1109 goto out;
1112 if (SSL_accept(ssl) != 1) {
1113 goto out;
1116 else {
1117 if (SSL_connect(ssl) != 1) {
1118 goto out;
1122 af = JimMakeChannel(interp, NULL, fd, NULL, "aio.sslstream%ld", af->addr_family, "r+");
1123 if (af == NULL) {
1124 goto out;
1127 af->ssl = ssl;
1128 af->fops = &ssl_fops;
1130 return JIM_OK;
1132 out:
1133 #if defined(HAVE_DUP)
1134 close(fd);
1135 #endif
1136 SSL_free(ssl);
1137 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1138 return JIM_ERR;
1141 static int aio_cmd_verify(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1143 AioFile *af = Jim_CmdPrivData(interp);
1144 int ret;
1146 if (!af->fops->verify) {
1147 return JIM_OK;
1150 ret = af->fops->verify(af);
1151 if (ret != JIM_OK) {
1152 if (JimCheckStreamError(interp, af)) {
1153 JimAioSetError(interp, af->filename);
1154 } else {
1155 Jim_SetResultString(interp, "failed to verify the connection authenticity", -1);
1158 return ret;
1160 #endif /* JIM_BOOTSTRAP */
1162 #if defined(HAVE_STRUCT_FLOCK) && !defined(JIM_BOOTSTRAP)
1163 static int aio_cmd_lock(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1165 AioFile *af = Jim_CmdPrivData(interp);
1166 struct flock fl;
1168 fl.l_start = 0;
1169 fl.l_len = 0;
1170 fl.l_type = F_WRLCK;
1171 fl.l_whence = SEEK_SET;
1173 switch (fcntl(af->fd, F_SETLK, &fl))
1175 case 0:
1176 Jim_SetResultInt(interp, 1);
1177 break;
1178 case -1:
1179 if (errno == EACCES || errno == EAGAIN)
1180 Jim_SetResultInt(interp, 0);
1181 else
1183 Jim_SetResultFormatted(interp, "lock failed: %s",
1184 strerror(errno));
1185 return JIM_ERR;
1187 break;
1188 default:
1189 Jim_SetResultInt(interp, 0);
1190 break;
1193 return JIM_OK;
1196 static int aio_cmd_unlock(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1198 AioFile *af = Jim_CmdPrivData(interp);
1199 struct flock fl;
1200 fl.l_start = 0;
1201 fl.l_len = 0;
1202 fl.l_type = F_UNLCK;
1203 fl.l_whence = SEEK_SET;
1205 Jim_SetResultInt(interp, fcntl(af->fd, F_SETLK, &fl) == 0);
1206 return JIM_OK;
1208 #endif /* JIM_BOOTSTRAP */
1210 static const jim_subcmd_type aio_command_table[] = {
1211 { "read",
1212 "?-nonewline? ?len?",
1213 aio_cmd_read,
1216 /* Description: Read and return bytes from the stream. To eof if no len. */
1218 { "copyto",
1219 "handle ?size?",
1220 aio_cmd_copy,
1223 /* Description: Copy up to 'size' bytes to the given filehandle, or to eof if no size. */
1225 { "gets",
1226 "?var?",
1227 aio_cmd_gets,
1230 /* Description: Read one line and return it or store it in the var */
1232 { "puts",
1233 "?-nonewline? str",
1234 aio_cmd_puts,
1237 /* Description: Write the string, with newline unless -nonewline */
1239 { "isatty",
1240 NULL,
1241 aio_cmd_isatty,
1244 /* Description: Is the file descriptor a tty? */
1246 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
1247 { "recvfrom",
1248 "len ?addrvar?",
1249 aio_cmd_recvfrom,
1252 /* Description: Receive up to 'len' bytes on the socket. Sets 'addrvar' with receive address, if set */
1254 { "sendto",
1255 "str address",
1256 aio_cmd_sendto,
1259 /* Description: Send 'str' to the given address (dgram only) */
1261 { "accept",
1262 "?addrvar?",
1263 aio_cmd_accept,
1266 /* Description: Server socket only: Accept a connection and return stream */
1268 { "listen",
1269 "backlog",
1270 aio_cmd_listen,
1273 /* Description: Set the listen backlog for server socket */
1275 #endif /* JIM_BOOTSTRAP */
1276 { "flush",
1277 NULL,
1278 aio_cmd_flush,
1281 /* Description: Flush the stream */
1283 { "eof",
1284 NULL,
1285 aio_cmd_eof,
1288 /* Description: Returns 1 if stream is at eof */
1290 { "close",
1291 "?r(ead)|w(rite)?",
1292 aio_cmd_close,
1295 JIM_MODFLAG_FULLARGV,
1296 /* Description: Closes the stream. */
1298 { "seek",
1299 "offset ?start|current|end",
1300 aio_cmd_seek,
1303 /* Description: Seeks in the stream (default 'current') */
1305 { "tell",
1306 NULL,
1307 aio_cmd_tell,
1310 /* Description: Returns the current seek position */
1312 { "filename",
1313 NULL,
1314 aio_cmd_filename,
1317 /* Description: Returns the original filename */
1319 #ifdef O_NDELAY
1320 { "ndelay",
1321 "?0|1?",
1322 aio_cmd_ndelay,
1325 /* Description: Set O_NDELAY (if arg). Returns current/new setting. */
1327 #endif
1328 #ifdef HAVE_FSYNC
1329 { "sync",
1330 NULL,
1331 aio_cmd_sync,
1334 /* Description: Flush and fsync() the stream */
1336 #endif
1337 { "buffering",
1338 "none|line|full",
1339 aio_cmd_buffering,
1342 /* Description: Sets buffering */
1344 #ifdef jim_ext_eventloop
1345 { "readable",
1346 "?readable-script?",
1347 aio_cmd_readable,
1350 /* Description: Returns script, or invoke readable-script when readable, {} to remove */
1352 { "writable",
1353 "?writable-script?",
1354 aio_cmd_writable,
1357 /* Description: Returns script, or invoke writable-script when writable, {} to remove */
1359 { "onexception",
1360 "?exception-script?",
1361 aio_cmd_onexception,
1364 /* Description: Returns script, or invoke exception-script when oob data, {} to remove */
1366 #endif
1367 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1368 { "ssl",
1369 "?-server cert priv?",
1370 aio_cmd_ssl,
1373 JIM_MODFLAG_FULLARGV
1374 /* Description: Wraps a stream socket with SSL/TLS and returns a new channel */
1376 { "verify",
1377 NULL,
1378 aio_cmd_verify,
1381 /* Description: Verifies the certificate of a SSL/TLS channel */
1383 #endif /* JIM_BOOTSTRAP */
1384 #if defined(HAVE_STRUCT_FLOCK) && !defined(JIM_BOOTSTRAP)
1385 { "lock",
1386 NULL,
1387 aio_cmd_lock,
1390 /* Description: Attempt to get a lock. */
1392 { "unlock",
1393 NULL,
1394 aio_cmd_unlock,
1397 /* Description: Relase a lock. */
1399 #endif /* JIM_BOOTSTRAP */
1400 { NULL }
1403 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1405 return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, aio_command_table, argc, argv), argc, argv);
1408 static int JimAioOpenCommand(Jim_Interp *interp, int argc,
1409 Jim_Obj *const *argv)
1411 const char *mode;
1413 if (argc != 2 && argc != 3) {
1414 Jim_WrongNumArgs(interp, 1, argv, "filename ?mode?");
1415 return JIM_ERR;
1418 mode = (argc == 3) ? Jim_String(argv[2]) : "r";
1420 #ifdef jim_ext_tclcompat
1422 const char *filename = Jim_String(argv[1]);
1424 /* If the filename starts with '|', use popen instead */
1425 if (*filename == '|') {
1426 Jim_Obj *evalObj[3];
1428 evalObj[0] = Jim_NewStringObj(interp, "::popen", -1);
1429 evalObj[1] = Jim_NewStringObj(interp, filename + 1, -1);
1430 evalObj[2] = Jim_NewStringObj(interp, mode, -1);
1432 return Jim_EvalObjVector(interp, 3, evalObj);
1435 #endif
1436 return JimMakeChannel(interp, NULL, -1, argv[1], "aio.handle%ld", 0, mode) ? JIM_OK : JIM_ERR;
1439 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1440 static void JimAioSslContextDelProc(struct Jim_Interp *interp, void *privData)
1442 SSL_CTX_free((SSL_CTX *)privData);
1443 ERR_free_strings();
1446 static SSL_CTX *JimAioSslCtx(Jim_Interp *interp)
1448 SSL_CTX *ssl_ctx = (SSL_CTX *)Jim_GetAssocData(interp, "ssl_ctx");
1449 if (ssl_ctx == NULL) {
1450 SSL_load_error_strings();
1451 SSL_library_init();
1452 ssl_ctx = SSL_CTX_new(TLSv1_2_method());
1453 if (ssl_ctx && SSL_CTX_set_default_verify_paths(ssl_ctx)) {
1454 SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
1455 Jim_SetAssocData(interp, "ssl_ctx", JimAioSslContextDelProc, ssl_ctx);
1456 } else {
1457 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1460 return ssl_ctx;
1462 #endif /* JIM_BOOTSTRAP */
1465 * Creates a channel for fh/fd/filename.
1467 * If fh is not NULL, uses that as the channel (and sets AIO_KEEPOPEN).
1468 * Otherwise, if fd is >= 0, uses that as the channel.
1469 * Otherwise opens 'filename' with mode 'mode'.
1471 * hdlfmt is a sprintf format for the filehandle. Anything with %ld at the end will do.
1472 * mode is used for open or fdopen.
1474 * Creates the command and sets the name as the current result.
1475 * Returns the AioFile pointer on sucess or NULL on failure.
1477 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
1478 const char *hdlfmt, int family, const char *mode)
1480 AioFile *af;
1481 char buf[AIO_CMD_LEN];
1482 int openFlags = 0;
1484 snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
1486 if (fh) {
1487 openFlags = AIO_KEEPOPEN;
1490 snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
1491 if (!filename) {
1492 filename = Jim_NewStringObj(interp, buf, -1);
1495 Jim_IncrRefCount(filename);
1497 if (fh == NULL) {
1498 #if !defined(JIM_ANSIC)
1499 if (fd >= 0) {
1500 fh = fdopen(fd, mode);
1502 else
1503 #endif
1504 fh = fopen(Jim_String(filename), mode);
1506 if (fh == NULL) {
1507 JimAioSetError(interp, filename);
1508 #if !defined(JIM_ANSIC)
1509 if (fd >= 0) {
1510 close(fd);
1512 #endif
1513 Jim_DecrRefCount(interp, filename);
1514 return NULL;
1518 /* Create the file command */
1519 af = Jim_Alloc(sizeof(*af));
1520 memset(af, 0, sizeof(*af));
1521 af->fp = fh;
1522 af->fd = fileno(fh);
1523 af->filename = filename;
1524 #ifdef FD_CLOEXEC
1525 if ((openFlags & AIO_KEEPOPEN) == 0) {
1526 (void)fcntl(af->fd, F_SETFD, FD_CLOEXEC);
1528 #endif
1529 af->openFlags = openFlags;
1530 af->addr_family = family;
1531 af->fops = &stdio_fops;
1532 af->ssl = NULL;
1534 Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc);
1536 /* Note that the command must use the global namespace, even if
1537 * the current namespace is something different
1539 Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, Jim_NewStringObj(interp, buf, -1)));
1541 return af;
1544 #if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && defined(HAVE_SYS_UN_H))
1546 * Create a pair of channels. e.g. from pipe() or socketpair()
1548 static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename,
1549 const char *hdlfmt, int family, const char *mode[2])
1551 if (JimMakeChannel(interp, NULL, p[0], filename, hdlfmt, family, mode[0])) {
1552 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
1553 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
1555 if (JimMakeChannel(interp, NULL, p[1], filename, hdlfmt, family, mode[1])) {
1556 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
1557 Jim_SetResult(interp, objPtr);
1558 return JIM_OK;
1562 /* Can only be here if fdopen() failed */
1563 close(p[0]);
1564 close(p[1]);
1565 JimAioSetError(interp, NULL);
1566 return JIM_ERR;
1568 #endif
1570 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
1572 static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1574 const char *hdlfmt = "aio.unknown%ld";
1575 const char *socktypes[] = {
1576 "unix",
1577 "unix.server",
1578 "dgram",
1579 "dgram.server",
1580 "stream",
1581 "stream.server",
1582 "pipe",
1583 "pair",
1584 NULL
1586 enum
1588 SOCK_UNIX,
1589 SOCK_UNIX_SERVER,
1590 SOCK_DGRAM_CLIENT,
1591 SOCK_DGRAM_SERVER,
1592 SOCK_STREAM_CLIENT,
1593 SOCK_STREAM_SERVER,
1594 SOCK_STREAM_PIPE,
1595 SOCK_STREAM_SOCKETPAIR,
1597 int socktype;
1598 int sock;
1599 const char *hostportarg = NULL;
1600 int res;
1601 int on = 1;
1602 const char *mode = "r+";
1603 int family = PF_INET;
1604 Jim_Obj *argv0 = argv[0];
1605 int ipv6 = 0;
1607 if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-ipv6")) {
1608 if (!IPV6) {
1609 Jim_SetResultString(interp, "ipv6 not supported", -1);
1610 return JIM_ERR;
1612 ipv6 = 1;
1613 family = PF_INET6;
1615 argc -= ipv6;
1616 argv += ipv6;
1618 if (argc < 2) {
1619 wrongargs:
1620 Jim_WrongNumArgs(interp, 1, &argv0, "?-ipv6? type ?address?");
1621 return JIM_ERR;
1624 if (Jim_GetEnum(interp, argv[1], socktypes, &socktype, "socket type", JIM_ERRMSG) != JIM_OK)
1625 return JIM_ERR;
1627 Jim_SetEmptyResult(interp);
1629 hdlfmt = "aio.sock%ld";
1631 if (argc > 2) {
1632 hostportarg = Jim_String(argv[2]);
1635 switch (socktype) {
1636 case SOCK_DGRAM_CLIENT:
1637 if (argc == 2) {
1638 /* No address, so an unconnected dgram socket */
1639 sock = socket(family, SOCK_DGRAM, 0);
1640 if (sock < 0) {
1641 JimAioSetError(interp, NULL);
1642 return JIM_ERR;
1644 break;
1646 /* fall through */
1647 case SOCK_STREAM_CLIENT:
1649 union sockaddr_any sa;
1650 int salen;
1652 if (argc != 3) {
1653 goto wrongargs;
1656 if (ipv6) {
1657 if (JimParseIPv6Address(interp, hostportarg, &sa, &salen) != JIM_OK) {
1658 return JIM_ERR;
1661 else if (JimParseIpAddress(interp, hostportarg, &sa, &salen) != JIM_OK) {
1662 return JIM_ERR;
1664 sock = socket(family, (socktype == SOCK_DGRAM_CLIENT) ? SOCK_DGRAM : SOCK_STREAM, 0);
1665 if (sock < 0) {
1666 JimAioSetError(interp, NULL);
1667 return JIM_ERR;
1669 res = connect(sock, &sa.sa, salen);
1670 if (res) {
1671 JimAioSetError(interp, argv[2]);
1672 close(sock);
1673 return JIM_ERR;
1676 break;
1678 case SOCK_STREAM_SERVER:
1679 case SOCK_DGRAM_SERVER:
1681 union sockaddr_any sa;
1682 int salen;
1684 if (argc != 3) {
1685 goto wrongargs;
1688 if (ipv6) {
1689 if (JimParseIPv6Address(interp, hostportarg, &sa, &salen) != JIM_OK) {
1690 return JIM_ERR;
1693 else if (JimParseIpAddress(interp, hostportarg, &sa, &salen) != JIM_OK) {
1694 return JIM_ERR;
1696 sock = socket(family, (socktype == SOCK_DGRAM_SERVER) ? SOCK_DGRAM : SOCK_STREAM, 0);
1697 if (sock < 0) {
1698 JimAioSetError(interp, NULL);
1699 return JIM_ERR;
1702 /* Enable address reuse */
1703 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
1705 res = bind(sock, &sa.sa, salen);
1706 if (res) {
1707 JimAioSetError(interp, argv[2]);
1708 close(sock);
1709 return JIM_ERR;
1711 if (socktype == SOCK_STREAM_SERVER) {
1712 res = listen(sock, 5);
1713 if (res) {
1714 JimAioSetError(interp, NULL);
1715 close(sock);
1716 return JIM_ERR;
1719 hdlfmt = "aio.socksrv%ld";
1721 break;
1723 #ifdef HAVE_SYS_UN_H
1724 case SOCK_UNIX:
1726 struct sockaddr_un sa;
1727 socklen_t len;
1729 if (argc != 3 || ipv6) {
1730 goto wrongargs;
1733 if (JimParseDomainAddress(interp, hostportarg, &sa) != JIM_OK) {
1734 JimAioSetError(interp, argv[2]);
1735 return JIM_ERR;
1737 family = PF_UNIX;
1738 sock = socket(PF_UNIX, SOCK_STREAM, 0);
1739 if (sock < 0) {
1740 JimAioSetError(interp, NULL);
1741 return JIM_ERR;
1743 len = strlen(sa.sun_path) + 1 + sizeof(sa.sun_family);
1744 res = connect(sock, (struct sockaddr *)&sa, len);
1745 if (res) {
1746 JimAioSetError(interp, argv[2]);
1747 close(sock);
1748 return JIM_ERR;
1750 hdlfmt = "aio.sockunix%ld";
1751 break;
1754 case SOCK_UNIX_SERVER:
1756 struct sockaddr_un sa;
1757 socklen_t len;
1759 if (argc != 3 || ipv6) {
1760 goto wrongargs;
1763 if (JimParseDomainAddress(interp, hostportarg, &sa) != JIM_OK) {
1764 JimAioSetError(interp, argv[2]);
1765 return JIM_ERR;
1767 family = PF_UNIX;
1768 sock = socket(PF_UNIX, SOCK_STREAM, 0);
1769 if (sock < 0) {
1770 JimAioSetError(interp, NULL);
1771 return JIM_ERR;
1773 len = strlen(sa.sun_path) + 1 + sizeof(sa.sun_family);
1774 res = bind(sock, (struct sockaddr *)&sa, len);
1775 if (res) {
1776 JimAioSetError(interp, argv[2]);
1777 close(sock);
1778 return JIM_ERR;
1780 res = listen(sock, 5);
1781 if (res) {
1782 JimAioSetError(interp, NULL);
1783 close(sock);
1784 return JIM_ERR;
1786 hdlfmt = "aio.sockunixsrv%ld";
1787 break;
1789 #endif
1791 #if defined(HAVE_SOCKETPAIR) && defined(HAVE_SYS_UN_H)
1792 case SOCK_STREAM_SOCKETPAIR:
1794 int p[2];
1795 static const char *mode[2] = { "r+", "r+" };
1797 if (argc != 2 || ipv6) {
1798 goto wrongargs;
1801 if (socketpair(PF_UNIX, SOCK_STREAM, 0, p) < 0) {
1802 JimAioSetError(interp, NULL);
1803 return JIM_ERR;
1805 return JimMakeChannelPair(interp, p, argv[1], "aio.sockpair%ld", PF_UNIX, mode);
1807 break;
1808 #endif
1810 #if defined(HAVE_PIPE)
1811 case SOCK_STREAM_PIPE:
1813 int p[2];
1814 static const char *mode[2] = { "r", "w" };
1816 if (argc != 2 || ipv6) {
1817 goto wrongargs;
1820 if (pipe(p) < 0) {
1821 JimAioSetError(interp, NULL);
1822 return JIM_ERR;
1825 return JimMakeChannelPair(interp, p, argv[1], "aio.pipe%ld", 0, mode);
1827 break;
1828 #endif
1830 default:
1831 Jim_SetResultString(interp, "Unsupported socket type", -1);
1832 return JIM_ERR;
1835 return JimMakeChannel(interp, NULL, sock, argv[1], hdlfmt, family, mode) ? JIM_OK : JIM_ERR;
1837 #endif /* JIM_BOOTSTRAP */
1840 * Returns the file descriptor of a writable, newly created temp file
1841 * or -1 on error.
1843 * On success, leaves the filename in the interpreter result, otherwise
1844 * leaves an error message.
1846 int Jim_MakeTempFile(Jim_Interp *interp, const char *template)
1848 #ifdef HAVE_MKSTEMP
1849 int fd;
1850 mode_t mask;
1851 Jim_Obj *filenameObj;
1853 if (template == NULL) {
1854 const char *tmpdir = getenv("TMPDIR");
1855 if (tmpdir == NULL || *tmpdir == '\0' || access(tmpdir, W_OK) != 0) {
1856 tmpdir = "/tmp/";
1858 filenameObj = Jim_NewStringObj(interp, tmpdir, -1);
1859 if (tmpdir[0] && tmpdir[strlen(tmpdir) - 1] != '/') {
1860 Jim_AppendString(interp, filenameObj, "/", 1);
1862 Jim_AppendString(interp, filenameObj, "tcl.tmp.XXXXXX", -1);
1864 else {
1865 filenameObj = Jim_NewStringObj(interp, template, -1);
1868 #if defined(S_IRWXG) && defined(S_IRWXO)
1869 mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
1870 #else
1871 /* MinGW does not have group/owner permissions */
1872 mask = umask(S_IXUSR);
1873 #endif
1875 /* Update the template name directly with the filename */
1876 fd = mkstemp(filenameObj->bytes);
1877 umask(mask);
1878 if (fd < 0) {
1879 JimAioSetError(interp, filenameObj);
1880 Jim_FreeNewObj(interp, filenameObj);
1881 return -1;
1884 Jim_SetResult(interp, filenameObj);
1885 return fd;
1886 #else
1887 Jim_SetResultString(interp, "platform has no tempfile support", -1);
1888 return -1;
1889 #endif
1892 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1893 static int JimAioLoadSSLCertsCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1895 SSL_CTX *ssl_ctx;
1897 if (argc != 2) {
1898 Jim_WrongNumArgs(interp, 1, argv, "dir");
1899 return JIM_ERR;
1902 ssl_ctx = JimAioSslCtx(interp);
1903 if (!ssl_ctx) {
1904 return JIM_ERR;
1906 if (SSL_CTX_load_verify_locations(ssl_ctx, NULL, Jim_String(argv[1])) == 1) {
1907 return JIM_OK;
1909 Jim_SetResultString(interp, ERR_error_string(ERR_get_error(), NULL), -1);
1910 return JIM_ERR;
1912 #endif /* JIM_BOOTSTRAP */
1914 int Jim_aioInit(Jim_Interp *interp)
1916 if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG))
1917 return JIM_ERR;
1919 #if defined(JIM_SSL)
1920 Jim_CreateCommand(interp, "load_ssl_certs", JimAioLoadSSLCertsCommand, NULL, NULL);
1921 #endif
1923 Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL);
1924 #ifndef JIM_ANSIC
1925 Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL);
1926 #endif
1928 /* Create filehandles for stdin, stdout and stderr */
1929 JimMakeChannel(interp, stdin, -1, NULL, "stdin", 0, "r");
1930 JimMakeChannel(interp, stdout, -1, NULL, "stdout", 0, "w");
1931 JimMakeChannel(interp, stderr, -1, NULL, "stderr", 0, "w");
1933 return JIM_OK;