|
|
1.1 root 1: /*
2: * tkRawTCP.c --
3: *
4: * This file contains a simple Tcl "connect" command
5: * that returns an standard Tcl File descriptor (as would
6: * be returned by Tcl_OpenCmd).
7: * Extended to create servers, accept connections, shutdown parts of full
8: * duplex connections and handle UNIX domain sockets.
9: *
10: * Author: Pekka Nikander <[email protected]>
11: * Modified: Tim MacKenzie <[email protected])
12: *
13: * Copyright 1992 Telecom Finland
14: *
15: * Permission to use, copy, modify, and distribute this
16: * software and its documentation for any purpose and without
17: * fee is hereby granted, provided that this copyright
18: * notice appears in all copies. Telecom Finland
19: * makes no representations about the suitability of this
20: * software for any purpose. It is provided "as is" without
21: * express or implied warranty.
22: *
23: * Created: Sun Mar 22 18:20:29 1992
24: * based on: Last modified: Sun Mar 22 21:34:31 1992 pnr
25: * Last modified: Mon Jun 29 15:25:14 EST 1992 tym
26: *
27: */
28:
29: #ifndef lint
30: static char rcsid[] = "...";
31: #endif /* not lint */
32:
33: #include "tclint.h"
34: #include "tclunix.h"
35:
36: #include <assert.h>
37: #include <string.h>
38: #include <sys/types.h>
39: #include <sys/socket.h>
40: #include <netinet/in.h>
41: #include <netdb.h>
42: #include <arpa/inet.h>
43: #include <sys/un.h>
44:
45: #include <tk.h>
46:
47: static int inet_connect _ANSI_ARGS_((char *host, char *port,int server));
48: static int unix_connect _ANSI_ARGS_((char *path, int server));
49: static void HandleSocket _ANSI_ARGS_ ((ClientData clientData, int mask));
50:
51: typedef struct {
52: Tcl_Interp *interp;
53: OpenFile *filePtr;
54: char *tclCmd;
55: char *fileId;
56: } FileCmd;
57:
58: /*
59: *------------------------------------------------------------------
60: *
61: * Tcp_MakeOpenFile --
62: *
63: * Set up on OpenFile structure in the interpreter for a newly
64: * opened file
65: *
66: * Results:
67: * none
68: *
69: * Side effects:
70: * Adds an OpenFile to the list.
71: *------------------------------------------------------------------
72: */
73:
74: /* ARGSUSED */
75: void
76: Tcp_MakeOpenFile(interp,fd,r,w)
77: Tcl_Interp *interp;
78: int fd;
79: int r,w;
80: {/* Create an OpenFile structure using f and install it in the interpreter with
81: * Readable and Writable set to r and w
82: */
83: Interp *iPtr = (Interp *) interp;
84: register OpenFile *filePtr;
85:
86: filePtr = (OpenFile *) ckalloc(sizeof(OpenFile));
87:
88: filePtr->f = NULL;
89: filePtr->f2 = NULL;
90:
91: /* Open the file with the correct type (doesn't handle !r && !w) */
92: #ifdef MSDOS
93: filePtr->f = fdopen(fd,(r&&w)?"rb+":(r?"rb":"wb"));
94: #else
95: filePtr->f = fdopen(fd,(r&&w)?"r+":(r?"r":"w"));
96: #endif
97: /* Don't do buffered communication if full-duplex... it breaks! */
98: if (r&w) setbuf(filePtr->f,0);
99:
100: filePtr->readable = r;
101: filePtr->writable = w;
102: filePtr->numPids = 0;
103: filePtr->pidPtr = NULL;
104: filePtr->errorId = -1;
105:
106: /*
107: * Enter this new OpenFile structure in the table for the
108: * interpreter. May have to expand the table to do this.
109: */
110:
111: TclMakeFileTable(iPtr, fd);
112: if (iPtr->filePtrArray[fd] != NULL) {
113: panic("Tcl_OpenCmd found file already open");
114: }
115: iPtr->filePtrArray[fd] = filePtr;
116: }
117:
118: /*
119: *------------------------------------------------------------------
120: *
121: * Tcp_ConnectCmd --
122: *
123: * Open a socket connection to a given host and service.
124: *
125: * Results:
126: * A standard Tcl result.
127: *
128: * Side effects:
129: * An open socket connection.
130: * Sets the global variable connect_info(file%d) to the obtained
131: * port when setting up server.
132: *------------------------------------------------------------------
133: */
134:
135: /* ARGSUSED */
136: int
137: Tcp_ConnectCmd(notUsed, interp, argc, argv)
138: ClientData notUsed;
139: Tcl_Interp *interp;
140: int argc;
141: char **argv;
142: {
143: Interp *iPtr = (Interp *) interp;
144: char *host,*port;
145: int fd;
146: int server=0;
147: int unicks = 0;
148:
149: if (argc != 2 && argc != 3 &&
150: (argc != 4 || (argc == 4 && strcmp(argv[1],"-server")))) {
151: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
152: "[{-server}] address_spec\"", (char *) NULL);
153: return TCL_ERROR;
154: }
155:
156: if (!strcmp(argv[1],"-server"))
157: server = 1;
158:
159: /*
160: * Create the connection
161: */
162: if (argc - server == 2) {/* Unix domain socket */
163: unicks = 1;
164: fd = unix_connect(argv[1+server],server);
165: } else
166: fd = inet_connect(argv[1+server], argv[2+server],server);
167:
168: if (fd < 0) {
169: /* Tell them why it fell apart */
170: if (unicks)
171: if (server)
172: Tcl_AppendResult(interp,
173: "Couldn't setup listening socket with path \"",
174: argv[1+server],"\" : ",Tcl_UnixError(interp),
175: (char *) NULL);
176: else
177: Tcl_AppendResult(interp,
178: "Couldn't connect to \"",argv[1],"\" : ",
179: Tcl_UnixError(interp),(char *) NULL);
180: else
181: if (server)
182: Tcl_AppendResult(interp,
183: "couldn't setup listening socket on port:",
184: atoi(argv[3])==0?"any":argv[3]," using address \"",
185: strlen(argv[2])?argv[2]:"anywhere.","\": ",
186: Tcl_UnixError(interp), (char *)NULL);
187: else
188: Tcl_AppendResult(interp, "couldn't open connection to \"",
189: argv[1], "\" port \"", argv[2], "\": ",
190: Tcl_UnixError(interp), (char *) NULL);
191: return TCL_ERROR;
192: }
193:
194: sprintf(interp->result, "file%d", fd);
195: if (server && !unicks) {
196: /* Find out what port we got */
197: char buf[50];
198: struct sockaddr_in sockaddr;
199: int res,len=sizeof(sockaddr);
200: res =getsockname(fd,(struct sockaddr *) &sockaddr, &len);
201: if (res < 0) {
202: sprintf(buf,"%d",errno);
203: } else
204: sprintf(buf,"%d",(int)ntohs(sockaddr.sin_port));
205: Tcl_SetVar2(interp,"connect_info",interp->result,buf,TCL_GLOBAL_ONLY);
206: }
207:
208: Tcp_MakeOpenFile(iPtr,fd,1,1-server);
209:
210: return TCL_OK;
211: }
212:
213: /*
214: *------------------------------------------------------------------
215: *
216: * Tcp_ShutdownCmd --
217: *
218: * Shutdown a socket for reading writing or both using shutdown(2)
219: *
220: * Results:
221: * standard tcl result.
222: *
223: * Side effects:
224: * Modifies the OpenFile structure appropriately
225: *------------------------------------------------------------------
226: */
227:
228: /* ARGSUSED */
229: int
230: Tcp_ShutdownCmd(notUsed, interp, argc, argv)
231: ClientData notUsed;
232: Tcl_Interp *interp;
233: int argc;
234: char **argv;
235: {
236: Interp *iPtr = (Interp *) interp;
237: OpenFile *filePtr;
238: register FILE *f;
239: int fd;
240:
241: if (argc != 3) {
242: wrong_args:
243: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
244: " fileid <option>\"", (char *) NULL);
245: return TCL_ERROR;
246: }
247:
248: if (TclGetOpenFile(interp, argv[1], &filePtr) != TCL_OK) {
249: return TCL_ERROR;
250: }
251:
252: f = filePtr->f;
253: fd = fileno(filePtr->f);
254: if (!strcmp(argv[2],"0") || !strcmp(argv[2],"receives") ||
255: !strcmp(argv[2],"read")) {
256: if (!filePtr->readable) {
257: Tcl_AppendResult(interp, "File is not readable",(char *) NULL);
258: return TCL_ERROR;
259: }
260: if (shutdown(fd,0)) {
261: Tcl_AppendResult(interp, "shutdown: ", Tcl_UnixError(interp),
262: (char *) NULL);
263: return TCL_ERROR;
264: }
265: filePtr->readable=0;
266: } else if (!strcmp(argv[2],"1") || !strcmp(argv[2],"sends") ||
267: !strcmp(argv[2],"write")) {
268: if (!filePtr->writable) {
269: Tcl_AppendResult(interp, "File is not writable",(char *) NULL);
270: return TCL_ERROR;
271: }
272: if (shutdown(fd,1)) {
273: Tcl_AppendResult(interp, "shutdown: ", Tcl_UnixError(interp),
274: (char *) NULL);
275: return TCL_ERROR;
276: }
277: filePtr->writable=0;
278: } else if (!strcmp(argv[2],"2") || !strcmp(argv[2],"all") ||
279: !strcmp(argv[2],"both")) {
280: if (shutdown(fd,2)) {
281: Tcl_AppendResult(interp, "shutdown: ", Tcl_UnixError(interp),
282: (char *) NULL);
283: return TCL_ERROR;
284: }
285: filePtr->writable=0;
286: filePtr->readable=0;
287: } else
288: goto wrong_args;
289: return TCL_OK;
290: }
291:
292:
293:
294: /*
295: *------------------------------------------------------------------
296: *
297: * Tcp_AcceptCmd --
298: *
299: * Accept a connection on a listening socket
300: *
301: * Results:
302: * a standard tcl result
303: *
304: * Side effects:
305: * Opens a new file.
306: * Sets the global variable connect_info(file%d) to a list
307: * containing the remote address (host ip, port) of the
308: * connector.
309: *------------------------------------------------------------------
310: */
311:
312: /* ARGSUSED */
313: int
314: Tcp_AcceptCmd(notUsed, interp, argc, argv)
315: ClientData notUsed;
316: Tcl_Interp *interp;
317: int argc;
318: char **argv;
319: {
320: Interp *iPtr = (Interp *) interp;
321: struct sockaddr_in sockaddr;
322: int len = sizeof sockaddr;
323: OpenFile *filePtr;
324: register FILE *f;
325: int fd;
326:
327: if (argc != 2) {
328: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
329: " listening_socket\"", (char *) NULL);
330: return TCL_ERROR;
331: }
332:
333: if (TclGetOpenFile(interp, argv[1], &filePtr) != TCL_OK) {
334: return TCL_ERROR;
335: }
336: if (!filePtr->readable) {
337: Tcl_AppendResult(interp, "\"", argv[1],
338: "\" wasn't opened for reading", (char *) NULL);
339: return TCL_ERROR;
340: }
341:
342: f = filePtr->f;
343: fd = fileno(filePtr->f);
344:
345: fd = accept(fd,(struct sockaddr *)&sockaddr,&len);
346: if (fd < 0) {
347: Tcl_AppendResult(interp, "system error in accept()", (char *)NULL);
348: return TCL_ERROR;
349: }
350:
351: {/* Set the global connect_info */
352: char buf[100];
353: char nm[10];
354: if (sockaddr.sin_family == AF_INET)
355: sprintf(buf,"%s %d",inet_ntoa(sockaddr.sin_addr),
356: ntohs(sockaddr.sin_port));
357: else
358: buf[0]=0; /* Empty string for UNIX domain sockets */
359: sprintf(nm,"file%d",fd);
360: Tcl_SetVar2(interp,"connect_info",nm,buf,TCL_GLOBAL_ONLY);
361: }
362:
363: /*
364: * Create the FILE*
365: */
366: Tcp_MakeOpenFile(iPtr,fd,1,1);
367:
368: sprintf(interp->result, "file%d", fd);
369: return TCL_OK;
370: }
371:
372: /*
373: *----------------------------------------------------------------
374: *
375: * unix_connect --
376: *
377: * Create a (unix_domain) fd connection using given rendeavous
378: *
379: * Results:
380: * An open fd or -1.
381: *
382: * Side effects:
383: * None.
384: *----------------------------------------------------------------
385: */
386:
387: static int
388: unix_connect(path,server)
389: char *path; /* Path name to create or use */
390: int server; /* 1->make server, 0->connect to server */
391: {
392: struct sockaddr_un sockaddr;
393: int sock, status;
394: extern int errno;
395:
396: sock = socket(PF_UNIX, SOCK_STREAM, 0);
397: if (sock < 0) {
398: return -1;
399: }
400:
401: sockaddr.sun_family = AF_UNIX;
402: strncpy(sockaddr.sun_path,path,sizeof(sockaddr.sun_path)-1);
403: sockaddr.sun_path[sizeof(sockaddr.sun_path)-1] = 0; /* Just in case */
404:
405: if (server)
406: status = bind(sock,(struct sockaddr *) &sockaddr, sizeof(sockaddr));
407: else
408: status = connect(sock, (struct sockaddr *) &sockaddr, sizeof(sockaddr));
409:
410: if (status < 0) {
411: close (sock);
412: return -1;
413: }
414:
415: if (server) {
416: listen(sock,5);
417: return sock;
418: }
419:
420: return sock;
421: }
422:
423: /*
424: *----------------------------------------------------------------
425: *
426: * inet_connect --
427: *
428: * Create a (inet domain) fd connection to given host and port.
429: *
430: * Results:
431: * An open fd or -1.
432: *
433: * Side effects:
434: * None.
435: *----------------------------------------------------------------
436: */
437:
438: static int
439: inet_connect(host, service,server)
440: char *host; /* Host to connect, name or IP address */
441: char *service; /* Port to use, service name or port number */
442: int server;
443: {
444: struct hostent *hostent, _hostent;
445: struct servent *servent, _servent;
446: struct protoent *protoent;
447: struct sockaddr_in sockaddr;
448: int sock, status;
449: int hostaddr, hostaddrPtr[2];
450: int servport;
451: extern int errno;
452:
453: hostent = gethostbyname(host);
454: if (hostent == NULL) {
455: hostaddr = inet_addr(host);
456: if (hostaddr == -1) {
457: if (server && !strlen(host))
458: hostaddr = INADDR_ANY;
459: else {
460: errno = EINVAL;
461: return -1;
462: }
463: }
464: _hostent.h_addr_list = (char **)hostaddrPtr;
465: _hostent.h_addr_list[0] = (char *)&hostaddr;
466: _hostent.h_addr_list[1] = NULL;
467: _hostent.h_length = sizeof(hostaddr);
468: _hostent.h_addrtype = AF_INET;
469: hostent = &_hostent;
470: }
471: servent = getservbyname(service, "tcp");
472: if (servent == NULL) {
473: servport = htons(atoi(service));
474: if (servport == -1) {
475: errno = EINVAL;
476: return -1;
477: }
478: _servent.s_port = servport;
479: _servent.s_proto = "tcp";
480: servent = &_servent;
481: }
482: protoent = getprotobyname(servent->s_proto);
483: if (protoent == NULL) {
484: errno = EINVAL;
485: return -1;
486: }
487:
488: sock = socket(PF_INET, SOCK_STREAM, protoent->p_proto);
489: if (sock < 0) {
490: return -1;
491: }
492:
493: sockaddr.sin_family = AF_INET;
494: memcpy((char *)&(sockaddr.sin_addr.s_addr),
495: (char *) hostent->h_addr_list[0],
496: (size_t) hostent->h_length);
497: sockaddr.sin_port = servent->s_port;
498:
499: if (server)
500: status = bind(sock,(struct sockaddr *) &sockaddr, sizeof(sockaddr));
501: else
502: status = connect(sock, (struct sockaddr *) &sockaddr, sizeof(sockaddr));
503:
504: if (status < 0) {
505: close (sock);
506: return -1;
507: }
508:
509: if (server) {
510: listen(sock,5);
511: return sock;
512: }
513:
514: return sock;
515: }
516:
517: /*
518: *----------------------------------------------------------------
519: *
520: * Tcp_FileHandlerCmd --
521: *
522: * Register a file handler with an open file. If there is
523: * already and existing handler, it will be no longer called.
524: * If no mask and command are given, any existing handler
525: * will be deleted.
526: *
527: * Results:
528: * A standard Tcl result. (Always OK).
529: *
530: * Side effects:
531: * A new file handler is associated with a give TCL open file.
532: * Whenever the file is readable, writeable and/or there is
533: * an expection condition on the file, a user supplied TCL
534: * command is called.
535: *
536: *----------------------------------------------------------------
537: */
538:
539: /* ARGSUSED */
540: int
541: Tcp_FileHandlerCmd(notUsed, interp, argc, argv)
542: ClientData notUsed;
543: Tcl_Interp *interp;
544: int argc;
545: char **argv;
546: {
547: FileCmd *cmdPtr;
548: OpenFile *filePtr;
549: int mask;
550:
551: if (argc != 2 && argc != 4) {
552: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
553: " fileId ?mode command?\"", (char *) NULL);
554: return TCL_ERROR;
555: }
556:
557: if (TclGetOpenFile(interp, argv[1], &filePtr) != TCL_OK) {
558: return TCL_ERROR;
559: }
560:
561: if (argc == 2) {
562: /*
563: * NOTE! Currently the cmdPtr structure will be left
564: * *unfreed* if the file handler is deleted
565: * via this code. Tough. Would need a hash table
566: * or something...
567: */
568: Tk_DeleteFileHandler(fileno(filePtr->f));
569: return TCL_OK;
570: }
571:
572: /*
573: * Find out on what situations the user is interested in.
574: * This is not the most elegant or efficient way to do this,
575: * but who cares? (I do, but not much enough :-)
576: */
577: mask = 0;
578: if (strchr(argv[2], 'r')) {
579: mask |= TK_READABLE;
580: }
581: if (strchr(argv[2], 'w')) {
582: mask |= TK_WRITABLE;
583: }
584: if (strchr(argv[2], 'e')) {
585: mask |= TK_EXCEPTION;
586: }
587: if (mask == 0 || (strlen(argv[2]) != strspn(argv[2], "rwe"))) {
588: Tcl_AppendResult(interp, "bad mask argument \"", argv[2],
589: "\": should be any combination of \"r\", \"w\" and \"e\"",
590: (char *) NULL);
591: fclose(filePtr->f);
592: return TCL_ERROR;
593: }
594:
595: cmdPtr = (FileCmd *)ckalloc(sizeof(FileCmd));
596: cmdPtr->interp = interp;
597: cmdPtr->filePtr = filePtr;
598: cmdPtr->tclCmd = ckalloc(strlen(argv[3]) + 1);
599: strcpy(cmdPtr->tclCmd, argv[3]);
600: cmdPtr->fileId = ckalloc(strlen(argv[1]) + 1);
601: strcpy(cmdPtr->fileId, argv[1]);
602:
603: /*
604: * NOTE! There may be an earlier file handler. Should do something.
605: */
606: Tk_CreateFileHandler(fileno(filePtr->f), mask, HandleSocket,
607: (ClientData) cmdPtr);
608:
609: return TCL_OK;
610: }
611: /*
612: *----------------------------------------------------------------
613: *
614: * HandleSocket --
615: *
616: * This procedure is called from Tk_DoOneEvent whenever there is
617: * a desired condition on a given open socket. An Tcl command
618: * given by the user is executed to handle the connection. If
619: * and EOF or ERROR condition is noticed, all memory resources
620: * associated with the socket are released and the socket is closed.
621: *
622: * Results:
623: * None.
624: *
625: * Side effects:
626: * The user supplied command can do anything.
627: *
628: *----------------------------------------------------------------
629: */
630:
631: static void
632: HandleSocket(clientData, mask)
633: ClientData clientData;
634: int mask;
635: {
636: int result;
637: FileCmd *cmdPtr = (FileCmd *) clientData;
638: OpenFile *filePtr = cmdPtr->filePtr;
639: Tcl_Interp *interp = cmdPtr->interp;
640: OpenFile *dummy;
641: int delete;
642: int fd = fileno(filePtr->f);
643:
644: Tk_Preserve((ClientData)cmdPtr);
645:
646: delete = 0;
647: if (TclGetOpenFile(interp, cmdPtr->fileId, &dummy) != TCL_OK) {
648: /* File is closed! */
649: Tcl_ResetResult(interp);
650: delete = 1;
651: } else {
652: assert(dummy == cmdPtr->filePtr);
653:
654: if (mask & TK_READABLE) {
655: result = Tcl_VarEval(interp, cmdPtr->tclCmd, " r ", cmdPtr->fileId,
656: (char *) NULL);
657: if (result != TCL_OK) {
658: TkBindError(interp);
659: }
660: }
661: if (mask & TK_WRITABLE) {
662: result = Tcl_VarEval(interp, cmdPtr->tclCmd, " w ", cmdPtr->fileId,
663: (char *) NULL);
664: if (result != TCL_OK) {
665: TkBindError(interp);
666: }
667: }
668: if (mask & TK_EXCEPTION) {
669: result = Tcl_VarEval(interp, cmdPtr->tclCmd, " e ", cmdPtr->fileId,
670: (char *) NULL);
671: if (result != TCL_OK) {
672: TkBindError(interp);
673: }
674: }
675:
676: if (feof(filePtr->f) || ferror(filePtr->f)) {
677: result = Tcl_VarEval(interp, "close ", cmdPtr->fileId,
678: (char *) NULL);
679: if (result != TCL_OK) {
680: TkBindError(interp);
681: }
682: delete = 1;
683: }
684: }
685:
686: Tk_Release((ClientData)cmdPtr);
687:
688: if (delete) {
689: Tk_DeleteFileHandler(fd);
690: Tk_EventuallyFree((ClientData)cmdPtr, (Tk_FreeProc *)free);
691: }
692: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.