#include "copyright.h" /* $XConsortium: XConnDis.c,v 11.36 88/09/16 11:09:34 jim Exp $ */ /* Copyright Massachusetts Institute of Technology 1985, 1986 */ #define NEED_EVENTS /* * THIS IS AN OS DEPENDENT FILE! It should work on 4.2BSD derived * systems. VMS and System V should plan to have their own version. */ #include <stdio.h> #include <X11/Xos.h> #include "Xlibint.h" #include <sys/socket.h> #ifndef hpux #include <netinet/tcp.h> #endif #ifdef hpux #include <sys/utsname.h> #endif #ifdef UNIXCONN #include <sys/un.h> #ifndef X_UNIX_PATH #define X_UNIX_PATH "/tmp/.X11-unix/X" #endif /* X_UNIX_PATH */ #endif /* UNIXCONN */ void bcopy(); #ifdef ISOCONN #include <isode/psap.h> #include <isode/tsap.h> #include <isode/isoservent.h> #ifdef ISODEBUG extern char *isodetcpath; int isodexbug = 0; #endif /* ISODEBUG */ #endif /* ISOCONN */ #ifdef ISOCONN /* * array of fd 2 family map so we can lookup right function below... * Its initialised at connection setup... */ int fd2family[MAXSOCKS]; /* * Globals for storing functions appropos each fd/socket type * UNIX_IO (0) map to sys calls * ISODE_IO (1) maps to my fns... */ extern int read(), TReadFromServer(); int (*readfn[])() = { read, TReadFromServer }; extern int write(), TWriteToServer(); int (*writefn[])() = { write, TWriteToServer }; extern int readv(), TReadvFromServer(); int (*readvfn[])() = { readv, TReadvFromServer }; extern writev(), TWritevToServer(); int (*writevfn[])() = { writev, TWritevToServer }; extern int UBytesReadable(), TBytesReadable(); int (*ioctlfn[])() = { UBytesReadable, TBytesReadable }; extern int close(), TDiscFromServer(); int (*closefn[])() = { close, TDiscFromServer }; static int iso_conn(server) char *server; { struct TSAPconnect tcs; struct TSAPconnect *tc = &tcs; struct TSAPdisconnect tds; struct TSAPdisconnect *td = &tds; struct QOStype qo; struct QOStype *qos = &qo; AEI aei; struct PSAPaddr *pa, rpa, *mpa, rmpa; int ret; char *x; extern char *index(); #ifdef ISODEBUG isodetcpath = ISODEPATH; #endif /* ISODEBUG */ /* * Get their TSAP from their AEI */ if ((x = index(server, ':')) != NULL) { *(x++) = '\0'; aei = str2aei(server, x); } else { aei = str2aei(server, DEFAULTTSERVICE); } if (aei == NULLAEI) { fprintf(stderr, "No AEI for %s\n", server); return -1; } if ((pa = aei2addr (aei)) == NULLPA) { fprintf (stderr, "address translation failed"); return -1; } rpa = *pa; /* struct copy */ /* * Get my address for completeness */ mpa = NULLPA; if (x != NULL) aei = str2aei(TLocalHostName(), x); else aei = str2aei(TLocalHostName(), DEFAULTTSERVICE); if (aei == NULLAEI) { fprintf (stderr, "my AEI lookup failed %s\n", TLocalHostName()); return -1; } if ((mpa = aei2addr (aei)) == NULLPA) { fprintf (stderr, "my address translation failed %s\n", TLocalHostName()); return -1; } rmpa = *mpa; /* struct copy */ /* * No Xpedited required ! */ tc->tc_expedited = 0; /* * Just a guess - i dont understand * the comment in the documentation about TSDUsizes */ tc->tc_tsdusize = 16000; tc->tc_cc = 0; qos->qos_reliability = LOW_QUALITY; /* Well this is X after all */ ret = TConnRequest(&(rmpa.pa_addr.sa_addr), &(rpa.pa_addr.sa_addr), 0, 0, NULLCP, qos, tc, td); /* * Should map errors to perrors somehow??? */ if (ret == NOTOK) { #ifdef ISODEBUG if (isodexbug) fprintf(stderr, "TCR Failed %s\n", TErrString(td->td_reason)); #endif /* ISODEBUG */ return -1; } else { #ifdef ISODEBUG if (isodexbug) fprintf(stderr, "client: isoconn ok\n"); #endif /* ISODEBUG */ ret = tc->tc_sd; return ret; } } #endif /* ISOCONN */ /* * Attempts to connect to server, given display name. Returns file descriptor * (network socket) or -1 if connection fails. The expanded display name * of the form hostname:number.screen ("::" if DECnet) is returned in a result * parameter. The screen number to use is also returned. */ int _XConnectDisplay (display_name, expanded_name, prop_name, screen_num) char *display_name; char *expanded_name; /* return */ char *prop_name; /* return */ int *screen_num; /* return */ { char displaybuf[256]; /* Display string buffer */ register char *display_ptr; /* Display string buffer pointer */ register char *numbuf_ptr; /* Server number buffer pointer */ char *screen_ptr; /* Pointer for locating screen num */ int display_num; /* Display number */ struct sockaddr_in inaddr; /* INET socket address. */ unsigned long hostinetaddr; /* result of inet_addr of arpa addr */ #ifdef UNIXCONN struct sockaddr_un unaddr; /* UNIX socket address. */ #endif struct sockaddr *addr; /* address to connect to */ struct hostent *host_ptr; int addrlen; /* length of address */ extern char *getenv(); extern struct hostent *gethostbyname(); int fd; /* Network socket */ char numberbuf[16]; char *dot_ptr = NULL; /* Pointer to . before screen num */ #ifdef DNETCONN int dnet = 0; char objname[20]; extern int dnet_conn(); #endif #ifdef ISOCONN int isoconn = 0; char isochar = 'X'; /* * ISO Host name are "host:T-Namenumber" * T-Name = T (for TCP) or X (for X.25) should really be * TP4 or TP0 whathaveyou */ #endif /* ISOCONN */ /* * Find the ':' seperator and extract the hostname and the * display number. * NOTE - if DECnet is to be used, the display name is formatted * as "host::number" */ (void) strncpy(displaybuf, display_name, sizeof(displaybuf)); if ((display_ptr = SearchString(displaybuf,':')) == NULL) return (-1); #ifdef DNETCONN if (*(display_ptr + 1) == ':') { dnet++; *(display_ptr++) = '\0'; } #endif #ifdef ISOCONN if ((*(display_ptr + 1) == 'X') || (*(display_ptr + 1) == 'T')) { isochar = *(display_ptr + 1); isoconn++; *(display_ptr++) = '\0'; } #endif /* ISOCONN */ *(display_ptr++) = '\0'; /* displaybuf now contains only a null-terminated host name, and * display_ptr points to the display number. * If the display number is missing there is an error. */ if (*display_ptr == '\0') return(-1); /* * Build a string of the form <display-number>.<screen-number> in * numberbuf, using ".0" as the default. */ screen_ptr = display_ptr; /* points to #.#.propname */ numbuf_ptr = numberbuf; /* beginning of buffer */ while (*screen_ptr != '\0') { if (*screen_ptr == '.') { /* either screen or prop */ if (dot_ptr) { /* then found prop_name */ screen_ptr++; break; } dot_ptr = numbuf_ptr; /* found screen_num */ *(screen_ptr++) = '\0'; *(numbuf_ptr++) = '.'; } else { *(numbuf_ptr++) = *(screen_ptr++); } } /* * If the spec doesn't include a screen number, add ".0" (or "0" if * only "." is present.) */ if (dot_ptr == NULL) { /* no screen num or prop */ dot_ptr = numbuf_ptr; *(numbuf_ptr++) = '.'; *(numbuf_ptr++) = '0'; } else { if (*(numbuf_ptr - 1) == '.') *(numbuf_ptr++) = '0'; } *numbuf_ptr = '\0'; /* * Return the screen number and property names in the result parameters */ *screen_num = atoi(dot_ptr + 1); strcpy (prop_name, screen_ptr); /* * Convert the server number string to an integer. */ display_num = atoi(display_ptr); /* * If the display name is missing, use current host. */ if (displaybuf[0] == '\0') #ifdef DNETCONN if (dnet) (void) strcpy (displaybuf, "0"); else #endif #ifdef UNIXCONN ; /* Do nothing if UNIX DOMAIN. Will be handled below. */ #else #ifdef hpux /* * same host name crock as in server and xinit. */ { struct utsname name; uname(&name); strcpy(displaybuf, name.nodename); } #else (void) gethostname (displaybuf, sizeof(displaybuf)); #endif /* hpux */ #endif /* UNIXCONN else TCPCONN (assumed) */ #ifdef DNETCONN if (dnet) { /* * build the target object name. */ sprintf(objname, "X$X%d", display_num); /* * Attempt to open the DECnet connection, return -1 if fails. */ if ((fd = dnet_conn(displaybuf, objname, SOCK_STREAM, 0, 0, 0, 0)) < 0) return(-1); /* errno set by dnet_conn. */ #ifdef ISOCONN else fd2family[fd] = UNIX_IO; #endif /* ISOCONN */ } else #endif #ifdef ISOCONN if (isoconn) { if ((fd = iso_conn(displaybuf)) < 0) return(-1); else fd2family[fd] = ISODE_IO; } else #endif { #ifdef UNIXCONN if ((displaybuf[0] == '\0') || (strcmp("unix", displaybuf) == 0)) { /* Connect locally using Unix domain. */ unaddr.sun_family = AF_UNIX; (void) strcpy(unaddr.sun_path, X_UNIX_PATH); strcat(unaddr.sun_path, display_ptr); addr = (struct sockaddr *) &unaddr; addrlen = strlen(unaddr.sun_path) + 2; /* * Open the network connection. */ if ((fd = socket((int) addr->sa_family, SOCK_STREAM, 0)) < 0) return(-1); /* errno set by system call. */ } else #endif { /* Get the statistics on the specified host. */ hostinetaddr = inet_addr (displaybuf); if (hostinetaddr == -1) { if ((host_ptr = gethostbyname(displaybuf)) == NULL) { /* No such host! */ errno = EINVAL; return(-1); } /* Check the address type for an internet host. */ if (host_ptr->h_addrtype != AF_INET) { /* Not an Internet host! */ errno = EPROTOTYPE; return(-1); } /* Set up the socket data. */ inaddr.sin_family = host_ptr->h_addrtype; #if defined(CRAY) && defined(OLDTCP) /* Only Cray UNICOS3 and UNICOS4 will define this */ { long t; bcopy((char *)host_ptr->h_addr, (char *)&t, sizeof(inaddr.sin_addr)); inaddr.sin_addr = t; } #else bcopy((char *)host_ptr->h_addr, (char *)&inaddr.sin_addr, sizeof(inaddr.sin_addr)); #endif /* CRAY and OLDTCP */ } else { #if defined(CRAY) && defined(OLDTCP) /* Only Cray UNICOS3 and UNICOS4 will define this */ inaddr.sin_addr = hostinetaddr; #else inaddr.sin_addr.s_addr = hostinetaddr; #endif /* CRAY and OLDTCP */ inaddr.sin_family = AF_INET; } addr = (struct sockaddr *) &inaddr; addrlen = sizeof (struct sockaddr_in); inaddr.sin_port = display_num; inaddr.sin_port += X_TCP_PORT; inaddr.sin_port = htons(inaddr.sin_port); /* * Open the network connection. */ if ((fd = socket((int) addr->sa_family, SOCK_STREAM, 0)) < 0) return(-1); /* errno set by system call. */ #ifdef ISOCONN fd2family[fd] = UNIX_IO; #endif /* ISOCONN */ /* make sure to turn off TCP coalescence */ #ifdef TCP_NODELAY { int mi = 1; setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, &mi, sizeof (int)); } #endif } if (connect(fd, addr, addrlen) == -1) { (void) close (fd); return(-1); /* errno set by system call. */ } } #ifdef ISOCONN if (!isoconn) { #endif /* ISOCONN */ /* * Not required for ISO T-Service */ /* * set it non-blocking. This is so we can read data when blocked * for writing in the library. */ #ifdef FIOSNBIO { int arg = 1; ioctl(fd, FIOSNBIO, &arg); } #else (void) fcntl(fd, F_SETFL, FNDELAY); #endif /* FIOSNBIO */ #ifdef ISOCONN } #endif /* ISOCONN */ /* * Return the id if the connection succeeded. Rebuild the expanded * spec and return it in the result parameter. */ display_ptr = displaybuf-1; while (*(++display_ptr) != '\0') ; *(display_ptr++) = ':'; #ifdef DNETCONN if (dnet) *(display_ptr++) = ':'; #endif #ifdef ISOCONN if (isoconn) { *(display_ptr++) = isochar; } #endif numbuf_ptr = numberbuf; while (*numbuf_ptr != '\0') *(display_ptr++) = *(numbuf_ptr++); if (prop_name[0] != '\0') { char *cp; *(display_ptr++) = '.'; for (cp = prop_name; *cp; cp++) *(display_ptr++) = *cp; } *display_ptr = '\0'; (void) strcpy(expanded_name, displaybuf); return(fd); } /* * Disconnect from server. */ int _XDisconnectDisplay (server) int server; { #ifdef ISOCONN (void)CloseToServer(server); #else /* ISOCONN */ (void) close(server); #endif /* ISOCONN */ } #undef NULL #define NULL ((char *) 0) /* * This is an OS dependent routine which: * 1) returns as soon as the connection can be written on.... * 2) if the connection can be read, must enqueue events and handle errors, * until the connection is writable. */ _XWaitForWritable(dpy) Display *dpy; { unsigned long r_mask[MSKCNT]; unsigned long w_mask[MSKCNT]; int nfound; CLEARBITS(r_mask); CLEARBITS(w_mask); while (1) { BITSET(r_mask, dpy->fd); BITSET(w_mask, dpy->fd); do { #ifdef ISOCONN /* * Note, we shouldnt get any connections to us as we're a client, * But the elements of style say we should check... * and of course, a TDR */ int vecp; char *vec[4]; struct TSAPdisconnect tds; struct TSAPdisconnect *td = &tds; nfound = TNetAccept(&vecp, vec, dpy->fd + 1, r_mask, w_mask, NULL, NOTOK, td); #ifdef ISODEBUG if (nfound < 0) { int sverrno = errno; fprintf(stderr, "TNetAcc err %d %s\n", errno, TErrString(td->td_reason)); errno = sverrno; } #endif /* ISODEBUG */ #ifdef ISODEBUG if (vecp > 0) { fprintf(stderr, "Weirdness, connect to a client?\n"); } #endif #else /* ISOCONN */ nfound = select (dpy->fd + 1, r_mask, w_mask, NULL, NULL); #endif /* ISOCONN */ if (nfound < 0 && errno != EINTR) (*_XIOErrorFunction)(dpy); } while (nfound <= 0); if (ANYSET(r_mask)) { char buf[BUFSIZE]; long pend_not_register; register long pend; register xEvent *ev; /* find out how much data can be read */ if (BytesReadable(dpy->fd, (char *) &pend_not_register) < 0) (*_XIOErrorFunction)(dpy); pend = pend_not_register; /* must read at least one xEvent; if none is pending, then we'll just block waiting for it */ if (pend < SIZEOF(xEvent)) pend = SIZEOF(xEvent); /* but we won't read more than the max buffer size */ if (pend > BUFSIZE) pend = BUFSIZE; /* round down to an integral number of XReps */ pend = (pend / SIZEOF(xEvent)) * SIZEOF(xEvent); _XRead (dpy, buf, pend); /* no space between comma and type or else macro will die */ STARTITERATE (ev,xEvent, buf, (pend > 0), (pend -= SIZEOF(xEvent))) { if (ev->u.u.type == X_Error) _XError (dpy, (xError *) ev); else /* it's an event packet; enqueue it */ _XEnq (dpy, ev); } ENDITERATE } if (ANYSET(w_mask)) return; } } _XWaitForReadable(dpy) Display *dpy; { unsigned long r_mask[MSKCNT]; int result; CLEARBITS(r_mask); do { #ifdef ISOCONN /* * Note, we shouldnt get any connections to us as we're a client, * But the elements of style say we should check... * and of course, a TDR */ int vecp; char *vec[4]; struct TSAPdisconnect tds; struct TSAPdisconnect *td = &tds; BITSET(r_mask, dpy->fd); result = TNetAccept(&vecp, vec, dpy->fd + 1, r_mask, NULL, NULL, NOTOK, td); #else /* ISOCONN */ BITSET(r_mask, dpy->fd); result = select(dpy->fd + 1, r_mask, NULL, NULL, NULL); #endif /* ISOCONN */ if (result == -1 && errno != EINTR) (*_XIOErrorFunction)(dpy); } while (result <= 0); } static int padlength[4] = {0, 3, 2, 1}; _XSendClientPrefix (dpy, client) Display *dpy; xConnClientPrefix *client; { /* * Authorization string stuff.... Must always transmit multiple of 4 * bytes. */ char *auth_proto = ""; int auth_length; char *auth_string = ""; int auth_strlen; char pad[3]; char buffer[BUFSIZ], *bptr; int bytes=0; auth_length = strlen(auth_proto); auth_strlen = strlen(auth_string); client->nbytesAuthProto = auth_length; client->nbytesAuthString = auth_strlen; bytes = (SIZEOF(xConnClientPrefix) + auth_length + padlength[auth_length & 3] + auth_strlen + padlength[auth_strlen & 3]); bcopy(client, buffer, SIZEOF(xConnClientPrefix)); bptr = buffer + SIZEOF(xConnClientPrefix); if (auth_length) { bcopy(auth_proto, bptr, auth_length); bptr += auth_length; if (padlength[auth_length & 3]) { bcopy(pad, bptr, padlength[auth_length & 3]); bptr += padlength[auth_length & 3]; } } if (auth_strlen) { bcopy(auth_string, bptr, auth_strlen); bptr += auth_strlen; if (padlength[auth_strlen & 3]) { bcopy(pad, bptr, padlength[auth_strlen & 3]); bptr += padlength[auth_strlen & 3]; } } (void) WriteToServer(dpy->fd, buffer, bytes); return; }