X-Git-Url: https://git.sesse.net/?p=rdpsrv;a=blobdiff_plain;f=Xserver%2Flib%2Fxtrans%2FXtransam.c;fp=Xserver%2Flib%2Fxtrans%2FXtransam.c;h=a52e18161a1b8ae347d88c6a3cb444d19a36c05e;hp=0000000000000000000000000000000000000000;hb=b6e6afccf37f4ad0515ef2a698f714fdf1bf23b3;hpb=e3340a110a3b01756b8e67531395a33b40a17d37 diff --git a/Xserver/lib/xtrans/Xtransam.c b/Xserver/lib/xtrans/Xtransam.c new file mode 100644 index 0000000..a52e181 --- /dev/null +++ b/Xserver/lib/xtrans/Xtransam.c @@ -0,0 +1,2008 @@ +/* $XConsortium: Xtransam.c,v 1.4 94/04/17 20:23:01 mor Exp $ */ +/* $XFree86: xc/lib/xtrans/Xtransam.c,v 3.1 1996/05/10 06:55:45 dawes Exp $ */ +/* + +Copyright (c) 1994 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the X Consortium. + +*/ + +/* Copyright (c) 1994 Vrije Universiteit Amsterdam, Netherlands + * + * All Rights Reserved + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name Vrije Universiteit not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The Vrije Universiteit + * makes no representations about the suitability of this software for + * any purpose. It is provided "as is" without express or implied warranty. + * + * THE VRIJE UNIVERSITEIT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL THE VRIJE UNIVERSITEIT BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This is the Amoeba implementation of the X Transport service layer + */ + +#define event am_event_t +#define interval am_interval_t +#define port am_port_t +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#undef event +#undef interval +#undef port + +extern char *strdup(); + +/* a new family for Amoeba RPC connections */ +#define AF_AMOEBA 33 +#define FamilyAmoeba 33 + +#define MAX_TCPIP_RETRY 4 +#define CIRCBUFSIZE 4096 /* was 1024 */ + +/* + * Amoeba channel description: + */ +typedef struct _XAmChanDesc { + int state; /* current state of connection */ + int type; /* type of connection */ + int status; /* status used by server */ + signum signal; /* signal to kill TCP/IP reader */ + semaphore *sema; /* select semaphore */ + struct vc *virtcirc; /* virtual circuit for Amoeba RPC */ + struct circbuf *circbuf; /* circular buffer for TCP/IP */ + capability chancap; /* TCP/IP channel capability */ + XtransConnInfo conninfo; /* back pointer to the connect info */ +} XAmChanDesc; + +/* Amoeba channel descriptor states */ +#define ACDS_FREE 0 /* unused */ +#define ACDS_USED 1 /* intermediate state */ +#define ACDS_CLOSED 2 /* just closed */ + +/* Amoeba channel types */ +#define ACDT_TCPIP 1 /* TCP/IP connection */ +#define ACDT_VIRTCIRC 2 /* Amoeba virtual circuit connection */ + + +#ifdef XSERV_t +#include "dix.h" /* clients[] needed by AmFindReadyClients */ +#define Error(list) ErrorF list +#define Fatal(list) FatalError list +#else +#define Error(list) printf list +#define Fatal(list) { printf list; exit(1); } +#endif + +#define dbprintf(list) /* printf list */ +#define doprintf(list) printf list /**/ + +/* + * First: utility functions. + */ + +#if defined(XSERV_t) || defined(FS_t) + +static semaphore main_sema; + +/* The X-server consists of one main thread, running the non re-entrant + * X code, and a number of auxilary threads that take care of reading + * the input streams, and input devices. The following set of routines + * wake up the main thread when it has something to do. + */ +void +InitMainThread() +{ + sema_init(&main_sema, 0); +} + +void +WakeUpMainThread() +{ + sema_up(&main_sema); +} + +int +SleepMainThread(timeout) +am_interval_t timeout; +{ + dbprintf(("Sleeping main thread timeout %d\n", timeout)); + return (sema_trydown(&main_sema, timeout) == 0) ? 0 : -1; +} + + +static int init_waiters; +static semaphore init_sema; + +void +AmInitWaitFor() +{ + init_waiters = 0; + sema_init(&init_sema, 0); +} + +/* + * Force caller thread to wait until main has finished the initialization. + */ +void +WaitForInitialization() +{ + init_waiters++; + dbprintf(("Waiting for initialization (%d)\n", init_waiters)); + sema_down(&init_sema); +} + +void +WakeupInitWaiters() +{ + /* wakeup threads in initial sleep */ + if (init_waiters > 0) { + dbprintf(("%d waiters wait for something ...\n", init_waiters)); + while (init_waiters-- > 0) { + sema_up(&init_sema); + } + } +} + +#endif /* XSERV_t || FS_t */ + + +#define THREAD_STACK_SIZE (8*1024) + +/* + * Amoeba connection information is stored in, so called, + * channel descriptors. Channel descriptors are identified + * by their index in the table below. + */ +static XAmChanDesc XAmChanDescriptors[OPEN_MAX]; +static void XAmCleanUpChanDesc(); /* forward */ + +/* + * Cleanup connection descriptors on a signal + */ +static void +XAmSignalCleanUpChanDesc(sig) + int sig; +{ + XAmCleanUpChanDesc(); + _exit(sig | 0x80); +} + +/* + * Cleanup connection descriptors + */ +static void +XAmCleanUpChanDesc() +{ + register int i; + + for (i = 0; i < OPEN_MAX; i++) { + switch (XAmChanDescriptors[i].type) { + case ACDT_TCPIP: + /* The Amoeba TCP/IP server is capability based, i.e. + * it uses capabilities to identify connections. Since a + * capability is only destroyed when it has aged too much + * or is explicitly deleted, the connection it identifies + * will tend to exist for some while even if the client is + * already gone. To force connections to close this loop + * destroys all open TCP/IP connection. This loop us auto- + * matically executed when exit() is called. + */ + std_destroy(&XAmChanDescriptors[i].chancap); + break; + case ACDT_VIRTCIRC: + /* Close the virtual circuit asynchronously, or otherwise + * we may hang for a minute under some (?) conditions. + */ + vc_close(XAmChanDescriptors[i].virtcirc, VC_BOTH | VC_ASYNC); + break; + } + XAmChanDescriptors[i].state = ACDS_FREE; + } +} + +/* + * Allocate a channel descriptor + */ +static XAmChanDesc * +XAmAllocChanDesc() +{ + register int i; + +#ifndef XSERV_t + + static int initialized = 0; + + /* + * Since the TCP/IP server is capability based its connections exists + * even if the owner process is long gone. To overcome this nuisance, + * a sweep is made over the connection descriptors when exit() is + * called or when an un-catched (by application program) signal is + * received. + */ + if (!initialized) { + initialized = 1; + atexit(XAmCleanUpChanDesc); + if (signal(SIGHUP, SIG_IGN) != SIG_IGN) + signal(SIGHUP, XAmSignalCleanUpChanDesc); + if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) + signal(SIGQUIT, XAmSignalCleanUpChanDesc); + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + signal(SIGINT, XAmSignalCleanUpChanDesc); + if (signal(SIGTERM, SIG_IGN) != SIG_IGN) + signal(SIGTERM, XAmSignalCleanUpChanDesc); + } +#endif + + for (i = 0; i < OPEN_MAX; i++) { + if (XAmChanDescriptors[i].state == ACDS_FREE) { + XAmChanDescriptors[i].state = ACDS_USED; + XAmChanDescriptors[i].conninfo = NULL; + return &XAmChanDescriptors[i]; + } + } + return NULL; +} + +/* + * Convert ``file descriptor'' to channel descriptor + */ +static XAmChanDesc * +XAmFdToChanDesc(fd) + int fd; +{ + if (fd >= 0 && fd < OPEN_MAX) { + return &XAmChanDescriptors[fd]; + } else { + return NULL; + } +} + +/* + * Convert channel descriptor to ``file descriptor'' + */ +static int +XAmChanDescToFd(chandesc) + XAmChanDesc *chandesc; +{ + return chandesc - XAmChanDescriptors; +} + +/* + * Free channel descriptor + */ +static void +XAmFreeChanDesc(chandesc) + XAmChanDesc *chandesc; +{ + if (chandesc->sema) { + xfree(chandesc->sema); + chandesc->sema = NULL; + } + chandesc->state = ACDS_FREE; +} + +static void XAmReaderThread(); + +static int +MakeAmConnection(phostname, idisplay, familyp, saddrlenp, saddrp) + char *phostname; + int idisplay; + int *familyp; /* RETURN */ + int *saddrlenp; /* RETURN */ + char **saddrp; /* RETURN */ +{ + capability xservercap; + char xserverpath[256]; + XAmChanDesc *chandesc; + errstat err; + + /* Amoeba requires a server hostname */ + if (phostname == NULL || *phostname == '\0') { + fprintf(stderr, "MakeAmConnection: Display name expected\n"); + return -1; + } + + /* allocate channel descriptor */ + chandesc = XAmAllocChanDesc(); + if (chandesc == NULL) { + fprintf(stderr, "MakeAmConnection: Out of channel capabilities\n"); + return -1; + } + + /* + * There are two possible way to make a connection on Amoeba. Either + * through an Amoeba RPC or a TCP/IP connection. Depending on whether + * the X server resides on Amoeba, Amoeba RPC's are used. Otherwise + * it uses a TCP/IP connection. + */ + (void)sprintf(xserverpath, "%s/%s:%d", DEF_XSVRDIR, phostname, idisplay); + if ((err = name_lookup(xserverpath, &xservercap)) == STD_OK) { + am_port_t vccaps[2]; + bufsize size; + errstat err; + header hdr; + + /* Amoeba virtual circuit connection */ + chandesc->type = ACDT_VIRTCIRC; + + /* get the two connection ports from the X-server */ + hdr.h_command = AX_CONNECT; + hdr.h_port = xservercap.cap_port; + hdr.h_priv = xservercap.cap_priv; + size = trans(&hdr, NILBUF, 0, &hdr, (char *)vccaps, sizeof(vccaps)); + if (ERR_STATUS(size)) { + err = ERR_CONVERT(size); + } else { + err = ERR_CONVERT(hdr.h_status); + } + if (err != STD_OK || size != sizeof(vccaps)) { + fprintf(stderr, "Xlib: connect to Amoeba X-server failed (%s)\n", + err_why(err)); + XAmFreeChanDesc(chandesc); + return -1; + } + + /* setup an Amoeba virtual circuit */ + chandesc->virtcirc = + vc_create(&vccaps[1], &vccaps[0], MAXBUFSIZE, MAXBUFSIZE); + if (chandesc->virtcirc == (struct vc *)NULL) { + fprintf(stderr, "Xlib: Amoeba virtual circuit create failed\n"); + XAmFreeChanDesc(chandesc); + return -1; + } + + /* Special Amoeba family type. For Amoeba no additional access control + * mechanism exists; when you have the server capability, you have + * the access. Just use a fake address. + */ + *familyp = AF_AMOEBA; + *saddrp = strdup("Amoeba"); + *saddrlenp = strlen(*saddrp); + } else { + char tcpname[256]; + capability tcpcap; + ipaddr_t ipaddr; + char *tcpsvr; + nwio_tcpcl_t tcpcl; + nwio_tcpconf_t tcpconf; + XAmChanDesc **param; + int result; + + /* Amoeba TCP/IP connection */ + chandesc->type = ACDT_TCPIP; + + /* lookup up TCP/IP server */ + if ((tcpsvr = getenv("TCP_SERVER")) == NULL) { + tcpsvr = TCP_SVR_NAME; + } + if ((err = name_lookup(tcpsvr, &tcpcap)) != STD_OK) { + fprintf(stderr, "Xlib: Cannot lookup %s (%s)\n", + tcpsvr, err_why(err)); + std_destroy(&chandesc->chancap); + XAmFreeChanDesc(chandesc); + return -1; + } + + /* establish TCP/IP connection */ + if ((err = tcpip_open(&tcpcap, &chandesc->chancap)) != STD_OK) { + fprintf(stderr, "Xlib: Cannot open TCP/IP server on %s (%s)\n", + tcpsvr, tcpip_why(err)); + std_destroy(&chandesc->chancap); + XAmFreeChanDesc(chandesc); + return -1; + } + + /* lookup TCP/IP hostname */ + if (isdigit(phostname[0])) { + ipaddr = inet_addr(phostname); + } else { + struct hostent *hp = gethostbyname(phostname); + if (hp == NULL) { + fprintf(stderr, "Xlib: %s unknown host\n", phostname); + return -1; + } + memcpy(&ipaddr, hp->h_addr, hp->h_length); + } + + /* set remote address/port on the TCP/IP connection */ + tcpconf.nwtc_flags = NWTC_SET_RA|NWTC_SET_RP|NWTC_LP_SEL; + tcpconf.nwtc_remaddr = ipaddr; + tcpconf.nwtc_remport = htons(6000+idisplay); + if ((err = tcp_ioc_setconf(&chandesc->chancap, &tcpconf)) != STD_OK) { + fprintf(stderr, "Xlib: Cannot configure TCP/IP server (%s)\n", + tcpip_why(err)); + std_destroy(&chandesc->chancap); + XAmFreeChanDesc(chandesc); + return -1; + } + + /* make the actual TCP/IP connection */ + tcpcl.nwtcl_flags = 0; + if ((err = tcp_ioc_connect(&chandesc->chancap, &tcpcl)) != STD_OK) { + fprintf(stderr, "Xlib: Cannot make TCP/IP connection (%s)\n", + tcpip_why(err)); + std_destroy(&chandesc->chancap); + XAmFreeChanDesc(chandesc); + return -1; + } + + /* start TCP/IP reader thread */ + chandesc->signal = sig_uniq(); + chandesc->circbuf = cb_alloc(CIRCBUFSIZE); + param = (XAmChanDesc **) xalloc(sizeof(XAmChanDesc *)); /* error checking? */ + *param = chandesc; + result = thread_newthread(XAmReaderThread, THREAD_STACK_SIZE, + (char *)param, sizeof(XAmChanDesc *)); + if (result == 0) { + fprintf(stderr, "Xlib: Cannot start reader thread\n"); + std_destroy(&chandesc->chancap); + XAmFreeChanDesc(chandesc); + return -1; + } + threadswitch(); /* give reader a try */ + + /* + * Family type is set to Internet so that the .Xauthority + * files from Unix will work under Amoeba (for Unix displays). + */ + *familyp = AF_INET; + *saddrlenp = sizeof(ipaddr_t); + *saddrp = xalloc(sizeof(ipaddr_t)); + memcpy(*saddrp, (char *)&ipaddr, sizeof(ipaddr_t)); /* error checking? */ + } + + return XAmChanDescToFd(chandesc); +} + +/* + * The TCP/IP server silently assumes a maximum buffer size of 30000 bytes. + */ +#define TCPIP_BUFSIZE 16384 + +static void +XAMCloseChannel(chandesc) +XAmChanDesc *chandesc; +{ + if (chandesc->state == ACDS_USED && chandesc->type == ACDT_TCPIP) { + cb_close(chandesc->circbuf); + chandesc->state = ACDS_CLOSED; + } +} + + +/* + * Shutdown TCP/IP reader thread + */ +static void +XAmReaderSignalCatcher(sig, us, extra) + signum sig; + thread_ustate *us; + _VOIDSTAR extra; +{ + register XAmChanDesc *chandesc = (XAmChanDesc *)extra; + + XAMCloseChannel(chandesc); + thread_exit(); +} + +/* + * TCP/IP reader thread + */ +static void +XAmReaderThread(argptr, argsize) + void *argptr; + int argsize; +{ + register XAmChanDesc *chandesc; + + chandesc = *((XAmChanDesc **)argptr); + (void) sig_catch(chandesc->signal, XAmReaderSignalCatcher, + (_VOIDSTAR) chandesc); + + while (chandesc->state == ACDS_USED) { + char buffer[CIRCBUFSIZE]; + bufsize size; + + size = tcpip_read(&chandesc->chancap, buffer, sizeof(buffer)); + if (ERR_STATUS(size) || size == 0) { + if (size == 0) { + static char msg[] = "Xlib: TCP/IP channel closed\n"; + + write(2, msg, sizeof(msg)); + } else { + fprintf(stderr, "Xlib: TCP/IP read failed (%s)\n", + err_why(ERR_CONVERT(size))); + } + XAMCloseChannel(chandesc); + break; + } + + if (cb_puts(chandesc->circbuf, buffer, size) != 0) { + fprintf(stderr, "Xlib: short write to circular buffer\n"); + XAMCloseChannel(chandesc); + } + } + + thread_exit(); +} + +/* + * Wait until input is available or until the timer expires. + */ +int +TRANS(AmSelect)(ifd, timout) + int ifd; + int timout; +{ + XAmChanDesc *chandesc; + int n; + + errno = 0; + + chandesc = XAmFdToChanDesc(ifd); + if (chandesc == NULL || chandesc->state != ACDS_USED) { + errno = EBADF; + return -1; + } + + if (chandesc->sema == NULL) { + /* Allocate semaphore to sleep on when no data is + * available. The underlying circular buffer and + * virtual circuit packages manage this semaphore. + */ + chandesc->sema = (semaphore *) xalloc(sizeof(semaphore)); + if (chandesc->sema == NULL) { + errno = ENOMEM; + return -1; + } + + sema_init(chandesc->sema, 0); + switch (chandesc->type) { + case ACDT_TCPIP: + cb_setsema(chandesc->circbuf, chandesc->sema); + break; + case ACDT_VIRTCIRC: + vc_setsema(chandesc->virtcirc, chandesc->sema); + break; + } + } + + switch (chandesc->type) { + case ACDT_TCPIP: + if ((n = cb_full(chandesc->circbuf)) != 0) { + if (n < 0) errno = EPIPE; + return n; /* includes error as well */ + } + if (sema_trydown(chandesc->sema, timout) < 0) { + errno = EINTR; + return -1; + } else { + /* we down for all the bytes in AMRead, so undo the down */ + sema_up(chandesc->sema); + } + if ((n = cb_full(chandesc->circbuf)) < 0) { + errno = EPIPE; + return -1; + } + return n; + + case ACDT_VIRTCIRC: + if ((n = vc_avail(chandesc->virtcirc, VC_IN)) != 0) { + if (n < 0) errno = EPIPE; + return n; /* includes error as well */ + } + if (sema_trydown(chandesc->sema, timout) < 0) { + errno = EINTR; + return -1; + } else { + /* we down for all the bytes in AMRead, so undo the down */ + sema_up(chandesc->sema); + } + if ((n = vc_avail(chandesc->virtcirc, VC_IN)) < 0) { + errno = EPIPE; + return -1; + } + return n; + } + + errno = EINVAL; + return -1; +} + +/* + * This function gets the local address of the transport and stores it in the + * XtransConnInfo structure for the connection. + */ + +static int +TRANS(AMGetAddr)(ciptr) +XtransConnInfo ciptr; +{ + PRMSG(1,"AMGetAddr(%x)\n", ciptr, 0,0 ); + PRMSG(1,"AMGetAddr: TODO\n", 0, 0, 0); + + return -1; +} + + +/* + * This function gets the remote address of the socket and stores it in the + * XtransConnInfo structure for the connection. + */ + +static int +TRANS(AMGetPeerAddr)(ciptr) +XtransConnInfo ciptr; +{ + struct nwio_tcpconf tcpconf; + errstat err; + XAmChanDesc *chandesc; + + PRMSG(2,"AMGetPeerAddr(%x)\n", ciptr, 0,0 ); + + chandesc = XAmFdToChanDesc(ciptr->fd); + if (chandesc == NULL || chandesc->state != ACDS_USED) { + errno = EBADF; + return -1; + } + + switch (chandesc->type) { + case ACDT_TCPIP: + /* get the remote adress from the TCP/IP server */ + if ((err = tcp_ioc_getconf(&chandesc->chancap, &tcpconf)) != STD_OK) { + PRMSG (1, "AMGetPeerAddr: Cannot get remote address (%d)\n", + (int) err, 0, 0); + return -1; + } + +#if 0 /* debug */ + { + struct hostent *remote; + char *hostname; + + remote = gethostbyaddr((char *) &tcpconf.nwtc_remaddr, + sizeof(tcpconf.nwtc_remaddr), AF_INET); + if ((remote == NULL) || (remote->h_name == NULL)) { + hostname = inet_ntoa(tcpconf.nwtc_remaddr); + } else { + hostname = remote->h_name; + } + PRMSG (1, "AMGetPeerAddr: remote addr `%s'\n", + hostname, 0, 0); + } +#endif + + ciptr->peeraddrlen = sizeof(tcpconf.nwtc_remaddr); + ciptr->peeraddr = (char *) xalloc (ciptr->peeraddrlen); + if (ciptr->peeraddr == NULL) { + PRMSG (1, "AMGetPeerAddr: Can't allocate peeraddr\n", + 0, 0, 0); + return -1; + } + + memcpy (ciptr->peeraddr, &tcpconf.nwtc_remaddr, ciptr->peeraddrlen); + break; + + case ACDT_VIRTCIRC: + /* for Amoeba virtual circuits just copy the client address */ + if ((ciptr->peeraddr = (char *) xalloc (ciptr->addrlen)) == NULL) { + PRMSG (1, "AMGetPeerAddr: Can't allocate peeraddr\n", + 0, 0, 0); + return -1; + } + + ciptr->peeraddrlen = ciptr->addrlen; + memcpy (ciptr->peeraddr, ciptr->addr, ciptr->peeraddrlen); + break; + } + + return 0; +} + + +static XtransConnInfo +TRANS(AMOpen)(device) +char *device; +{ + PRMSG(1,"AMOpen(%s)\n", device, 0,0 ); + PRMSG(1,"AMOpen: TODO\n", 0, 0, 0); + + return NULL; +} + + +static int +TRANS(AMAddrToNetbuf)(tlifamily, host, port, netbufp) +int tlifamily; +char *host; +char *port; +struct netbuf *netbufp; +{ + PRMSG(1,"AMAddrToNetbuf(%d,%s,%s)\n", tlifamily, host, port ); + PRMSG(1,"AMAddrToNetbuf: TODO\n", 0, 0, 0); + + return -1; +} + +/* + * These functions are the interface supplied in the Xtransport structure + */ + +#ifdef TRANS_CLIENT + +static XtransConnInfo +TRANS(AMOpenCOTSClient)(thistrans, protocol, host, port) +Xtransport *thistrans; +char *protocol; +char *host; +char *port; +{ + XtransConnInfo ciptr; + XAmChanDesc *chandesc; + + PRMSG(2,"AMOpenCOTSClient(%s,%s,%s)\n", protocol, host, port ); + + ciptr = (XtransConnInfo) xcalloc (1, sizeof(struct _XtransConnInfo)); + if (ciptr == NULL) { + PRMSG (1, "AMOpenCotsClient: malloc failed\n", 0, 0, 0); + return NULL; + } + + ciptr->fd = MakeAmConnection (host, 0 /* TODO */, &ciptr->family, + &ciptr->addrlen, &ciptr->addr); + if (ciptr->fd < 0) { + PRMSG(1,"AMOpenCOTSClient: Unable to make connection to %s\n", + host, 0,0 ); + xfree(ciptr); + return NULL; + } + + /* set the back pointer */ + chandesc = XAmFdToChanDesc(ciptr->fd); + chandesc->conninfo = ciptr; + + TRANS(AMGetPeerAddr)(ciptr); + + PRMSG(2,"AMOpenCOTSClient: made connection to %s; fd = %d, family = %d\n", + host, ciptr->fd, ciptr->family); + + return ciptr; +} + +#endif /* TRANS_CLIENT */ + +#if defined(XSERV_t) || defined(FS_t) + +/* The following defines come from osdep.h; + * they should removed from there eventually. + */ + +/* + * Some fundamental constants + */ +#define CONNECTOR_STACK 4000 /* stack for connector task */ +#define DEVREADER_STACK 4000 /* stack for device reader */ +#define CREATOR_STACK 4000 /* stack for connection creator */ +#define MAXTASKS 100 /* Maximum # clients */ + +/* + * OsComm status bits + */ +#define CONN_KILLED 0x1 /* Connection being closed */ +#define REQ_PUSHBACK 0x2 /* Request pushed back */ +#define IGNORE 0x4 /* True if client ignored */ +#define CONN_INIT 0x8 /* True if still initializing */ +#define CONN_ALIVE 0x10 /* True if living */ + + +#define REPLY_BUFSIZE 30000 + +capability X; /* X capability */ +char *XServerHostName; /* X server host name */ +char *XTcpServerName; /* TCP/IP server name */ + +static XtransConnInfo NewConns[MAXTASKS]; /* new client connections */ +int nNewConns; /* # of new clients */ + +int maxClient; /* Highest allocated client fd + 1*/ +static int minClient = 1; /* Lowest allocated client fd */ + +static char * +OsCommStatus(status) + int status; +{ + static char buf[100]; + + buf[0] = '\0'; + if (status == 0) + sprintf(buf, "NONE"); + if (status & CONN_INIT) + sprintf(buf, "%s INIT", buf); + if (status & CONN_ALIVE) + sprintf(buf, "%s ALIVE", buf); + if (status & CONN_KILLED) + sprintf(buf, "%s KILLED", buf); + if (status & REQ_PUSHBACK) + sprintf(buf, "%s PUSHBACK", buf); + if (status & IGNORE) + sprintf(buf, "%s IGNORE", buf); + return buf; +} + +static char * +OsCommFamily(family) + int family; +{ + if (family == FamilyAmoeba) { + return "AMOEBA"; + } else { + return "TCP/IP"; + } +} + + +/* + * Return status information about the open connections + */ +static errstat +ConnectionStatus(hdr, buf, size) + header *hdr; + char *buf; + int size; +{ + char *begin, *end; + char *bprintf(); + int fd; + XAmChanDesc *chandesc; + + begin = buf; + end = buf + size; + + + /* all active clients */ + begin = bprintf(begin, end, "Active clients:\n"); + for (fd = minClient; fd < maxClient; fd++) { + static XAmChanDesc *chandesc; + + chandesc = XAmFdToChanDesc(fd); + if (chandesc != NULL && chandesc->conninfo != NULL) { + begin = bprintf(begin, end, "%d: Family %s, State %d, Status %s\n", + fd, OsCommFamily(chandesc->conninfo->family), + chandesc->state, OsCommStatus(chandesc->status)); + } + } + + if (begin == NULL) { + hdr->h_size = 0; + return STD_SYSERR; + } else { + hdr->h_size = begin - buf; + return STD_OK; + } +} + +/* + * Wakeup main thread if necessary + */ +static void +UnblockMain(fd) +int fd; +{ + XAmChanDesc *chandesc; + + chandesc = XAmFdToChanDesc(fd); + if (chandesc != NULL) { + if ((chandesc->status & IGNORE) == 0) { + WakeUpMainThread(); + } + } else { + Error(("UnblockMain: invalid fd %d\n", fd)); + } +} + +static void +TcpIpReaderSignalCatcher(sig, us, extra) + signum sig; + thread_ustate *us; + _VOIDSTAR extra; +{ + XAmChanDesc *chandesc = (XAmChanDesc *) extra; + + if (chandesc->conninfo != NULL) { + dbprintf(("TcpIpReaderSignalCatcher(%d), number %d\n", + sig, chandesc->conninfo->fd)); + if (chandesc->signal != sig) { + Error(("TCP/IP Reader: Connection %s got unexpected signal %d\n", + chandesc->conninfo->fd, sig)); + } + } + + chandesc->signal = -1; + thread_exit(); +} + +void +TcpIpReaderThread(argptr, argsize) + void *argptr; + int argsize; +{ + XAmChanDesc *chandesc; + + if (argsize != sizeof(XAmChanDesc *)) { + Fatal(("Internal error: TcpIpReaderThread incorrectly called\n")); + } + + chandesc = * ((XAmChanDesc **) argptr); + (void) sig_catch(chandesc->signal, TcpIpReaderSignalCatcher, + (_VOIDSTAR) chandesc); + + for (;;) { + char buffer[MAXBUFSIZE]; + bufsize size; + + size = tcpip_read(&chandesc->chancap, buffer, sizeof(buffer)); + + dbprintf(("TcpIpReaderThread() read %d bytes\n", size)); + if (ERR_STATUS(size)) { + Error(("TCP/IP read failed (%s)\n", tcpip_why(ERR_CONVERT(size)))); + chandesc->status |= CONN_KILLED; + chandesc->status &= ~CONN_ALIVE; + chandesc->signal = -1; + thread_exit(); + } + + if (size == 0 || cb_puts(chandesc->circbuf, buffer, size)) { + if (size != 0) { + Error(("TCP/IP short write to circular buffer for %d\n", + XAmChanDescToFd(chandesc))); + } else { + Error(("TCP/IP read failed for client %d\n", + XAmChanDescToFd(chandesc))); + } + + chandesc->status |= CONN_KILLED; + chandesc->status &= ~CONN_ALIVE; + chandesc->signal = -1; + thread_exit(); + } + UnblockMain(XAmChanDescToFd(chandesc)); + } +} + +static XAmChanDesc * +AllocClientChannel() +{ + XAmChanDesc *chandesc; + int fd; + + chandesc = XAmAllocChanDesc(); + if (chandesc == NULL) { + return NULL; + } + + fd = XAmChanDescToFd(chandesc); + if (fd >= maxClient) { + maxClient = fd + 1; + dbprintf(("new max Client: %d\n", fd)); + } + if (fd < minClient) { + minClient = fd; + } + + return chandesc; +} + +static errstat +AmRegisterRPCconn(client_ports, server_ports) +am_port_t client_ports[2]; +am_port_t server_ports[2]; +{ + XAmChanDesc *chandesc; + + if ((chandesc = AllocClientChannel()) == NULL) { + return STD_NOSPACE; + } + + chandesc->type = ACDT_VIRTCIRC; + chandesc->virtcirc = vc_create(&server_ports[0], &server_ports[1], + MAXBUFSIZE, MAXBUFSIZE); + if (chandesc->virtcirc == NULL) { + Error(("Connection refused: No memory for virtual circuit\n")); + XAmFreeChanDesc(chandesc); + return STD_NOSPACE; + } + + dbprintf(("Amoeba connection registered\n")); + + vc_warn(chandesc->virtcirc, VC_IN, UnblockMain, XAmChanDescToFd(chandesc)); + + chandesc->status = CONN_INIT; + + /* cause WaitFor to call EstablishNewConnections: */ + nNewConns++; + WakeUpMainThread(); + + return STD_OK; +} + +static XAmChanDesc * +XAmFetchConnectingClient() +{ + XAmChanDesc *chandesc; + int fd; + + for (fd = minClient; fd < maxClient; fd++) { + chandesc = XAmFdToChanDesc(fd); + + if (chandesc->status & CONN_INIT) { + Error(("Client %d is connecting\n", fd)); + chandesc->status &= ~CONN_INIT; + return chandesc; + } + } + + return NULL; +} + +static errstat +AmRegisterTCPconn(chancap) +capability *chancap; +{ + XAmChanDesc *chandesc, **param; + + if ((chandesc = AllocClientChannel()) == NULL) { + return STD_NOSPACE; + } + + chandesc->type = ACDT_TCPIP; + chandesc->chancap = *chancap; + + if ((chandesc->circbuf = cb_alloc(MAXBUFSIZE)) == NULL) { + Error(("TCPconn refused: No memory for circular buffer\n")); + XAmFreeChanDesc(chandesc); + return STD_NOSPACE; + } + + chandesc->signal = sig_uniq(); + param = (XAmChanDesc **) xalloc(sizeof(XAmChanDesc *)); /* error checking? */ + *param = chandesc; + if (thread_newthread(TcpIpReaderThread, MAXBUFSIZE + CONNECTOR_STACK, + (char *)param, sizeof(XAmChanDesc *)) == 0) + { + Error(("TCPconn refused: Cannot start reader thread\n")); + cb_close(chandesc->circbuf); + cb_free(chandesc->circbuf); + XAmFreeChanDesc(chandesc); + return STD_NOSPACE; + } + + dbprintf(("TCP connection registered\n")); + + chandesc->status = CONN_INIT; + + /* cause WaitFor to call EstablishNewConnections: */ + nNewConns++; + WakeUpMainThread(); + + return STD_OK; +} + + +/* + * Establishing a new connection is done in two phases. This thread does the + * first part. It filters out bad connect requests. A new rendevous port is + * sent to the client and the main loop is informed if there is a legal + * request. The sleep synchronizes with the main loop so that the paperwork + * is finished for the current connect request before the thread is ready to + * accept another connect. + */ +static void +AmConnectorThread() +{ + header req, rep; + am_port_t client_ports[2]; + am_port_t server_ports[2]; + short s; + char *repb; + extern CreateNewClient(); + + WaitForInitialization(); + dbprintf(("AmConnectorThread() running ...\n")); + if ((repb = (char *)xalloc(REPLY_BUFSIZE)) == NULL) { + Fatal(("Amoeba connector thread: malloc failed")); + } + for (;;) { + do { + req.h_port = X.cap_port; + s = getreq(&req, NILBUF, 0); + } while (ERR_CONVERT(s) == RPC_ABORTED); + if (ERR_STATUS(s)) + Fatal(("Amoeba connector thread: getreq failed")); + + /* TODO: check privilege fields here */ + + dbprintf(("AmConnectorThread() accepting a request\n")); + + switch (req.h_command) { + + case STD_INFO: + rep.h_status = STD_OK; + sprintf(repb, "X11R6 server on %s", XServerHostName); + rep.h_size = strlen(repb); + putrep(&rep, repb, rep.h_size); + break; + + case STD_STATUS: + rep.h_status = ConnectionStatus(&rep, repb, REPLY_BUFSIZE); + putrep(&rep, repb, rep.h_size); + break; + +#ifdef XSERV_t + case AX_SHUTDOWN: + rep.h_status = STD_OK; + putrep(&rep, NILBUF, 0); + GiveUp(SIGTERM); + break; + + case AX_REINIT: + rep.h_status = STD_OK; + putrep(&rep, NILBUF, 0); + AutoResetServer(SIGINT); + break; +#endif + + case AX_CONNECT: + uniqport(&client_ports[0]); + uniqport(&server_ports[1]); + priv2pub(&client_ports[0], &server_ports[0]); + priv2pub(&server_ports[1], &client_ports[1]); + + rep.h_status = AmRegisterRPCconn(client_ports, server_ports); + if (rep.h_status == STD_OK) { + putrep(&rep, (bufptr)client_ports, 2*sizeof(am_port_t)); + } else { + putrep(&rep, NILBUF, 0); + } + break; + + default: + rep.h_status = STD_COMBAD; + putrep(&rep, NILBUF, 0); + break; + } + } +} + +#ifdef XSERV_t + +/* + * To prevent the X-server from generating lots of error messages, + * in case the server is gone or when its full. + */ +#define LOOP_OPEN 1 +#define LOOP_SETCONF 2 +#define LOOP_LISTEN 4 + +extern char *display; /* The display number */ + +/* + * The TCP/IP connector thread listens to a well known port (6000 + + * display number) for connection request. When such a request arrives + * it allocates a communication structure and a reader thread. This + * thread prevents the main loop from blocking when there's no data. + */ +static void +AmTCPConnectorThread() +{ + capability svrcap, chancap; + nwio_tcpconf_t tcpconf; + nwio_tcpcl_t tcpconnopt; + char name[BUFSIZ]; + errstat err; + int result; + int looping = 0; + + strncpy(name, XTcpServerName, BUFSIZ); + if ((err = name_lookup(name, &svrcap)) != STD_OK) { + sprintf(name, "%s/%s", TCP_SVR_NAME, XTcpServerName); + if ((err = name_lookup(name, &svrcap)) != STD_OK) + Fatal(("Lookup %s failed: %s\n", XTcpServerName, err_why(err))); + } + + WaitForInitialization(); + dbprintf(("AmTCPConnectorThread() running ...\n")); + + for (;;) { + /* + * Listen to TCP/IP port X_TCP_PORT + display for connections. + * Some interesting actions have to be taken to keep this connection + * alive and kicking :-) + */ + if ((err = tcpip_open(&svrcap, &chancap)) != STD_OK) { + /* the server probably disappeared, just wait for it to return */ + if (looping & LOOP_OPEN) { + Error(("TCP/IP open failed: %s\n", tcpip_why(err))); + looping |= LOOP_OPEN; + } + sleep(60); + (void) name_lookup(name, &svrcap); + continue; + } + looping &= ~LOOP_OPEN; + + tcpconf.nwtc_locport = htons(X_TCP_PORT + atoi(display)); + tcpconf.nwtc_flags = NWTC_EXCL | NWTC_LP_SET | NWTC_UNSET_RA | + NWTC_UNSET_RP; + if ((err = tcp_ioc_setconf(&chancap, &tcpconf)) != STD_OK) { + /* couldn't configure, probably server space problem */ + if (looping & LOOP_SETCONF) { + Error(("TCP/IP setconf failed: %s\n", tcpip_why(err))); + looping |= LOOP_SETCONF; + } + std_destroy(&chancap); + sleep(60); + continue; + } + looping &= ~LOOP_SETCONF; + + tcpconnopt.nwtcl_flags = 0; + if ((err = tcp_ioc_listen(&chancap, &tcpconnopt)) != STD_OK) { + /* couldn't listen, definitely a server memory problem */ + if (looping & LOOP_LISTEN) { + Error(("TCP/IP listen failed: %s\n", tcpip_why(err))); + looping |= LOOP_LISTEN; + } + std_destroy(&chancap); + sleep(60); + continue; + } + looping &= ~LOOP_LISTEN; + + if ((err = tcpip_keepalive_cap(&chancap)) != STD_OK) { + Error(("TCP/IP keep alive failed: %s\n", tcpip_why(err))); + std_destroy(&chancap); + continue; + } + + err = AmRegisterTCPconn(&chancap); + if (err != STD_OK) { + Error(("AmRegisterTCPconn failed (%s)\n", err_why(err))); + std_destroy(&chancap); + } + } +} + +static void +AmStartXserverThreads(chandesc) +XAmChanDesc *chandesc; +{ + char host[100]; + errstat err; + capability pubX; + static int threadsStarted = 0; + + /* + * Each time the server is reset this routine is called to + * setup the new well known sockets. For Amoeba we'll just + * keep using the old threads that are already running. + */ + if (!threadsStarted) { + threadsStarted = 1; + + /* + * Create a new capability for this X server + */ + if (XServerHostName == NULL) + XServerHostName = getenv("XHOST"); + if (XServerHostName == NULL) { + Fatal(("XHOST not set, or server host name not given\n")); + } + sprintf(host, "%s/%s:%s", DEF_XSVRDIR, XServerHostName, display); + + uniqport(&X.cap_port); + priv2pub(&X.cap_port, &pubX.cap_port); + (void) name_delete(host); + if ((err = name_append(host, &pubX)) != 0) { + Error(("Cannot create capability %s: %s\n", host, err_why(err))); + exit(1); + } + + /* Allow WaitFor module to initialize */ + AmInitWaitFor(); + + /* Also, initialize main thread locking */ + InitMainThread(); + + /* Initialize and start IOP reader thread */ + InitializeIOPServerReader(); + + /* Start native Amoeba service threads */ + if (thread_newthread(AmConnectorThread, CONNECTOR_STACK, 0, 0) <= 0) { + Fatal(("Cannot start Amoeba connector thread\n")); + } + if (thread_newthread(AmConnectorThread, CONNECTOR_STACK, 0, 0) <= 0) { + Fatal(("Cannot start Amoeba connector thread\n")); + } + chandesc->type = ACDT_VIRTCIRC; + chandesc->status = CONN_ALIVE; + + /* + * Start TCP/IP service threads + */ + if (XTcpServerName) { + if (thread_newthread(AmTCPConnectorThread, + CONNECTOR_STACK, 0, 0) <= 0) + Fatal(("Cannot start TCP connector thread\n")); + if (thread_newthread(AmTCPConnectorThread, + CONNECTOR_STACK, 0, 0) <= 0) + Fatal(("Cannot start TCP connector thread\n")); + } + } +} + +int +AmFindReadyClients(pClientsReady, mask) +int *pClientsReady; +long *mask; +{ + /* Check for clients needing attention. They may have input, + * or they might be dying. Ignore the clients not present in + * the file descriptor bit vector `mask'. This is used for + * implementing server grabs. + * Returns the number of clients having data for us. + */ + extern int ConnectionTranslation[]; + XAmChanDesc *chandesc; + int fd; + int nready; + + /* Since we're scheduled non-preemptively by default, allow the + * listener threads to run first, if needed: + */ + threadswitch(); + + nready = 0; + for (fd = minClient; fd < maxClient; fd++) { + int which; + int n; + + if (fd > 0 && (fd % 32) == 0) { + /* switch to next fd mask */ + mask++; + } + + if ((*mask & (1L << fd)) == 0) { + dbprintf(("skip %d\n", fd)); + continue; + } + + chandesc = XAmFdToChanDesc(fd); + if (chandesc->state != ACDS_USED) { + dbprintf(("AmFindReady: fd %d not in use\n", fd)); + continue; + } + + which = ConnectionTranslation[fd]; + dbprintf(("checking client %d (fd %d) of %d\n", + fd, which, maxClient)); + + if (chandesc->status & CONN_KILLED) { + dbprintf(("conn killed; close client with fd %d\n", fd)); + CloseDownClient(clients[which]); + chandesc->status &= ~(CONN_KILLED | CONN_ALIVE); + continue; + } + + if ((chandesc->status & CONN_ALIVE) == 0) { + dbprintf(("conn with %d is not alive\n", fd)); + continue; + } + + /* see if there is data available */ + switch (chandesc->type) { + case ACDT_TCPIP: + n = cb_full(chandesc->circbuf); + break; + case ACDT_VIRTCIRC: + n = vc_avail(chandesc->virtcirc, VC_IN); + break; + default: + n = -1; + } + + if (n < 0) { + dbprintf(("avail %d; close client %d\n", n, which)); + CloseDownClient(clients[which]); + continue; + } else { + if (n > 0) { + *pClientsReady++ = which; + nready++; + dbprintf(("client %d has %d bytes available\n", which, n)); + } else { + dbprintf(("client %d has no data available\n", which, n)); + } + } + + /* Clients that already have (possibly inserted) data are found + * with help from io.c (the ClientsWithData bit array). + */ + } + + return nready; +} + +#endif /* XSERV_t */ + +#endif /* XSERV_t || FS_t */ + +static +TRANS(AmSetAddr)(ciptr, chandesc) + XtransConnInfo ciptr; + XAmChanDesc *chandesc; +{ + switch (chandesc->type) { + case ACDT_TCPIP: + /* should really ask the TCP/IP server */ + ciptr->family = AF_INET; + ciptr->addr = strdup("XXXXTODO"); + ciptr->addrlen = strlen("XXXXTODO"); + break; + case ACDT_VIRTCIRC: + /* For Amoeba connections the adress is not really used, + * so just fake something + */ + ciptr->family = AF_AMOEBA; + ciptr->addr = strdup("Amoeba"); + ciptr->addrlen = strlen(ciptr->addr); + break; + } +} + +#ifdef TRANS_SERVER + +static XtransConnInfo +TRANS(AMOpenCOTSServer)(thistrans, protocol, given_host, port) +Xtransport *thistrans; +char *protocol; +char *given_host; +char *port; +{ + XAmChanDesc *chandesc; + XtransConnInfo ciptr; + + PRMSG(2,"AMOpenCOTSServer(%s,%s,%s)\n", protocol, given_host, port); + + ciptr = (XtransConnInfo) xcalloc (1, sizeof(struct _XtransConnInfo)); + if (ciptr == NULL) { + PRMSG (1, "AMOpenCotsClient: malloc failed\n", 0, 0, 0); + return NULL; + } + + chandesc = XAmAllocChanDesc(); + if (chandesc == NULL) { + return NULL; + } + +#ifdef XSERV_t + AmStartXserverThreads(chandesc); +#endif + + chandesc->conninfo = ciptr; + ciptr->fd = XAmChanDescToFd(chandesc); + + TRANS(AmSetAddr)(ciptr, chandesc); + TRANS(AMGetPeerAddr)(ciptr); + + return ciptr; +} + +#endif /* TRANS_SERVER */ + + +#ifdef TRANS_CLIENT + +static XtransConnInfo +TRANS(AMOpenCLTSClient)(thistrans, protocol, host, port) +Xtransport *thistrans; +char *protocol; +char *host; +char *port; +{ + XtransConnInfo ciptr; + int i; + + PRMSG(1,"AMOpenCLTSClient(%d,%s,%s)\n", protocol, host, port ); + /* TODO */ + return NULL; +} + +#endif /* TRANS_CLIENT */ + + +#ifdef TRANS_SERVER + +static XtransConnInfo +TRANS(AMOpenCLTSServer)(thistrans, protocol, host, port) +Xtransport *thistrans; +char *protocol; +char *host; +char *port; +{ + XtransConnInfo ciptr; + int i; + + PRMSG(1,"AMOpenCLTSServer(%d,%s,%s)\n", protocol, host, port ); + /* TODO */ + return NULL; +} + +static int +TRANS(AMResetListener)(ciptr) +XtransConnInfo ciptr; +{ + PRMSG(2,"AMResetListener()\n", 0, 0, 0 ); + + /* nothing to do? */ + return 0; +} + +#endif /* TRANS_SERVER */ + +static +TRANS(AMSetOption)(ciptr, option, arg) +XtransConnInfo ciptr; +int option; +int arg; +{ + PRMSG(1,"AMSetOption(%d,%d,%d)\n", ciptr->fd, option, arg ); + /* TODO */ + return -1; +} + + +#ifdef TRANS_SERVER + +static +TRANS(AMCreateListener)(ciptr, req) +XtransConnInfo ciptr; +char *req; +{ + PRMSG(2,"AMCreateListener(%x->%d,%x)\n", ciptr, ciptr->fd, req ); + + /* Listener threads are already created at this point */ + return 0; +} + + +static XtransConnInfo +TRANS(AMAccept)(ciptr) +XtransConnInfo ciptr; +{ + XAmChanDesc *chandesc; + XtransConnInfo newciptr; + + PRMSG(2,"AMAccept(%x->%d)\n", ciptr, ciptr->fd, 0 ); + +#if defined(XSERV_t) || defined(FS_t) + chandesc = XAmFetchConnectingClient(); + if (chandesc == NULL) { + PRMSG (1, "AMAccept: no client waiting?\n", 0, 0, 0); + return NULL; + } + nNewConns--; + + newciptr = (XtransConnInfo) xcalloc (1, sizeof(struct _XtransConnInfo)); + if (newciptr == NULL) + { + PRMSG (1, "AMAccept: malloc failed\n", 0, 0, 0); + return NULL; + } + + newciptr->fd = XAmChanDescToFd(chandesc); + chandesc->conninfo = newciptr; + chandesc->status |= CONN_ALIVE; + + PRMSG(2,"AMAccept: OK: (%x->%d)\n", newciptr, newciptr->fd, 0 ); + + TRANS(AmSetAddr)(newciptr, chandesc); + TRANS(AMGetPeerAddr)(newciptr); + + return newciptr; +#else + return NULL; +#endif +} + +#endif /* TRANS_SERVER */ + + +#ifdef TRANS_CLIENT + +static +TRANS(AMConnect)(ciptr, host, port) +XtransConnInfo ciptr; +char *host; +char *port; +{ + /* If this function is called, we are already connected */ + PRMSG(2, "AMConnect(%d,%s)\n", ciptr->fd, host, 0); + return 0; +} + +#endif /* TRANS_CLIENT */ + + +int +TRANS(AmFdBytesReadable)(fd, count) +int fd; +BytesReadable_t *count; +{ + register XAmChanDesc *chandesc; + + PRMSG(2, "AmFdBytesReadable(%d,%x): ", fd, count, 0 ); + +#ifndef XSERV_t + /* give reader threads a chance: */ + threadswitch(); +#endif + + errno = 0; + chandesc = XAmFdToChanDesc(fd); + if (chandesc == NULL || chandesc->state != ACDS_USED) { + errno = EBADF; + *count = 0; + return -1; + } + + switch (chandesc->type) { + case ACDT_TCPIP: + *count = cb_full(chandesc->circbuf); + break; + case ACDT_VIRTCIRC: + *count = vc_avail(chandesc->virtcirc, VC_IN); + break; + } + + if (*count < 0) { + errno = (chandesc->state == ACDS_CLOSED) ? EINTR : EPIPE; + *count = 0; + return -1; + } + + PRMSG(2, "AMFdBytesReadable: %d\n", *count, 0, 0 ); + + return 0; +} + +static +TRANS(AMBytesReadable)(ciptr, count) +XtransConnInfo ciptr; +BytesReadable_t *count; +{ + return TRANS(AmFdBytesReadable)(ciptr->fd, count); +} + + +static +TRANS(AMRead)(ciptr, buf, count) +XtransConnInfo ciptr; +char *buf; +int count; +{ + int fdi; + register XAmChanDesc *chandesc; + register int rv; + BytesReadable_t avail; + + fdi = ciptr->fd; + PRMSG(2, "AMRead(%d,%x,%d)\n", ciptr->fd, buf, count ); + + errno = 0; + chandesc = XAmFdToChanDesc(fdi); + if (chandesc == NULL || chandesc->state != ACDS_USED) { + errno = EBADF; + return -1; + } + + /* do a non-blocking read (maybe only conditionally?) */ + if ((TRANS(AMBytesReadable)(ciptr, &avail)) == 0) { + if (avail <= 0) { + PRMSG(2, "AMRead: nothing available yet\n", 0, 0, 0); + errno = EAGAIN; + return 0; + } else if (count > avail) { + PRMSG(2, "AMRead(%d): only %d of %d available\n", + ciptr->fd, avail, count); + count = avail; /* just read amount available */ + } + } else { + PRMSG(1, "AMRead: ...BytesReadable failed\n", 0, 0, 0); + return -1; + } + + switch (chandesc->type) { + case ACDT_TCPIP: + rv = cb_gets(chandesc->circbuf, buf, count, count); + if (rv != count) { + if (rv == 0) { + fprintf(stderr, "Xlib: Cannot read circbuf\n"); + errno = EPIPE; + rv = -1; + } else { + fprintf(stderr, "Xlib: Cannot read circbuf (%d)\n", rv); + } + } + break; + + case ACDT_VIRTCIRC: + rv = vc_readall(chandesc->virtcirc, buf, count); + if (rv < 0) { + fprintf(stderr, "Xlib: Cannot read virtual circuit\n"); + errno = EPIPE; + rv = -1; + } + break; + } + + /* The circular buffer writer will only UP the semaphore when + * characters are available; we have to down it ourselfs. + */ + if (chandesc->sema && rv > 0) + sema_mdown(chandesc->sema, rv); + + PRMSG(2, "AMRead: %d bytes\n", rv, 0, 0); + + return rv; +} + + +static +TRANS(AMWrite)(ciptr, buf, count) +XtransConnInfo ciptr; +char *buf; +int count; +{ + register XAmChanDesc *chandesc; + register int written; + + PRMSG(2, "AMWrite(%d,%x,%d)\n", ciptr->fd, buf, count ); + + errno = 0; + written = 0; + + chandesc = XAmFdToChanDesc(ciptr->fd); + if (chandesc == NULL || chandesc->state != ACDS_USED) { + errno = EBADF; + return -1; + } + + switch (chandesc->type) { + case ACDT_TCPIP: + while (count > 0) { + bufsize bsize; + int wrcnt; + + wrcnt = count > TCPIP_BUFSIZE ? TCPIP_BUFSIZE : count; + bsize = tcpip_write(&chandesc->chancap, buf, wrcnt); + if (ERR_STATUS(bsize)) { + fprintf(stderr, "Xlib: TCP/IP write failed (%s)\n", + tcpip_why(ERR_CONVERT(bsize))); + errno = EPIPE; + return -1; + } + if (bsize != wrcnt) { + fprintf(stderr, + "Xlib: TCP/IP write failed (expected %d, wrote %d)\n", + (int)bsize, wrcnt); + errno = EPIPE; + return -1; + } + buf += bsize; + count -= (int) bsize; + written += (int) bsize; + } + break; + + case ACDT_VIRTCIRC: + if ((written = vc_write(chandesc->virtcirc, buf, count)) < 0) { + fprintf(stderr, "Xlib: virtual circuit write failed\n"); + errno = EPIPE; + return -1; + } + break; + } + + return written; +} + + +static +TRANS(AMReadv)(ciptr, iov, n) +XtransConnInfo ciptr; +struct iovec *iov; +int n; +{ + int i; + int count = 0, thiscount; + + PRMSG(2, "AMReadv(%d,%x,%d)\n", ciptr->fd, ciptr, n ); + + for (i = 0; i < n; i++, iov++) { + if (iov->iov_len) { + thiscount = TRANS(AMRead)(ciptr, iov->iov_base, iov->iov_len); + if (thiscount < 0) return thiscount; + count += thiscount; + if (thiscount < iov->iov_len) break; + } + } + + return count; +} + + +static +TRANS(AMWritev)(ciptr, iov, n) +XtransConnInfo ciptr; +struct iovec *iov; +int n; +{ + int i; + int count = 0, thiscount; + + PRMSG(2, "AMWritev(%d,%x,%d)\n", ciptr->fd, iov, n ); + + for (i = 0; i < n; i++, iov++) { + if (iov->iov_len) { + thiscount = TRANS(AMWrite)(ciptr, iov->iov_base, iov->iov_len); + if (thiscount < 0) + return thiscount; + count += thiscount; + if (thiscount < iov->iov_len) break; + } + } + + return count; +} + + +static +TRANS(AMDisconnect)(ciptr) +XtransConnInfo ciptr; +{ + register XAmChanDesc *chandesc; + + PRMSG(2, "AMDisconnect(%x->%d)\n", ciptr, ciptr->fd, 0 ); + + chandesc = XAmFdToChanDesc(ciptr->fd); + if (chandesc != NULL) { + switch (chandesc->type) { + case ACDT_TCPIP: + if (chandesc->signal != -1) { + sig_raise(chandesc->signal); + chandesc->signal = -1; + } + std_destroy(&chandesc->chancap); + break; + + case ACDT_VIRTCIRC: + vc_close(chandesc->virtcirc, VC_BOTH | VC_ASYNC); + break; + } +#ifdef XSERV_t + if (ciptr->fd == maxClient - 1) { + maxClient--; + /* we could look if maxClient can be reduced even more */ + } +#endif + XAmFreeChanDesc(chandesc); + } + + return 0; +} + + +static +TRANS(AMClose)(ciptr) +XtransConnInfo ciptr; +{ + PRMSG(2, "AMClose(%x->%d)\n", ciptr, ciptr->fd, 0 ); + + return TRANS(AMDisconnect)(ciptr); +} + + +Xtransport TRANS(AmConnFuncs) = { + /* Combined AMOEBA RPC/TCP Interface; maybe we should split this */ + "amcon", + 0, +#ifdef TRANS_CLIENT + TRANS(AMOpenCOTSClient), +#endif /* TRANS_CLIENT */ +#ifdef TRANS_SERVER + TRANS(AMOpenCOTSServer), +#endif /* TRANS_SERVER */ +#ifdef TRANS_CLIENT + TRANS(AMOpenCLTSClient), +#endif /* TRANS_CLIENT */ +#ifdef TRANS_SERVER + TRANS(AMOpenCLTSServer), +#endif /* TRANS_SERVER */ + TRANS(AMSetOption), +#ifdef TRANS_SERVER + TRANS(AMCreateListener), + TRANS(AMResetListener), + TRANS(AMAccept), +#endif /* TRANS_SERVER */ +#ifdef TRANS_CLIENT + TRANS(AMConnect), +#endif /* TRANS_CLIENT */ + TRANS(AMBytesReadable), + TRANS(AMRead), + TRANS(AMWrite), + TRANS(AMReadv), + TRANS(AMWritev), + TRANS(AMDisconnect), + TRANS(AMClose), +};