summaryrefslogtreecommitdiffstats
path: root/xpa.c
diff options
context:
space:
mode:
Diffstat (limited to 'xpa.c')
-rw-r--r--xpa.c4720
1 files changed, 4720 insertions, 0 deletions
diff --git a/xpa.c b/xpa.c
new file mode 100644
index 0000000..d9c94e4
--- /dev/null
+++ b/xpa.c
@@ -0,0 +1,4720 @@
+/*
+ * Copyright (c) 1999-2003 Smithsonian Astrophysical Observatory
+ */
+
+#include <xpap.h>
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ *
+ * Private Routines and Data
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+/* this is the head of the global list -- too lazy to do anything more */
+static XPA xpahead=NULL;
+
+/* globals for the xpa environment, controlled by environment variables */
+static int stimeout=XPA_SHORT_TIMEOUT; /* select, gets timeout in secs */
+static int ltimeout=XPA_LONG_TIMEOUT; /* fillbuf timeout in secs */
+static int ctimeout=XPA_CONNECT_TIMEOUT;/* local xpans connect */
+static int verbosity=XPA_VERBOSITY; /* 0=quiet, 1=normal, 2=full */
+static int nsdelay=XPA_NSDELAY; /* delay increment waiting for ns to start */
+static int retries=XPA_RETRIES; /* retries when retrying */
+static int guseacl=1; /* 0=don't use acls, 1=enable acls */
+static int etimestamp=0; /* 0=don't timestamp errors, 1=do timestamp */
+static int nsregister=1; /* 0=don't register with xpans, 1=register */
+static int sigusr1=1; /* 0=don't use sigusr1, 1=enable sigusr1 */
+static int vercheck=1; /* 0=don't check version, 1=check version */
+static char *tmpdir=NULL; /* temporary dir for logs. etc. */
+#if HAVE_ATEXIT
+static int atexitinit=0; /* whether atexit has been registered */
+#endif
+
+/* variables used by all XPAs in this process */
+static char activefds[FD_SETSIZE];
+static char nsmethod[SZ_LINE];
+static char nsusers[SZ_LINE];
+
+/* global method type: unix or inet */
+static int mtype=0;
+
+/* width of select channel flag */
+static int swidth=FD_SETSIZE;
+
+/* defined in tcp.c */
+extern int use_localhost;
+
+/* erro code strings -- must match the error codes in xpap.h */
+char *xpaMessbuf[] = {
+ "oh, what a mess we've made!",
+ "authentication failed",
+ "xpa connection refused",
+ "can't resolve host name for xpa",
+ "can't read initialization info for xpa",
+ "invalid xpa command in initialization string",
+ "no receive function defined for this xpa",
+ "no send function defined for this xpa",
+ "no info function defined for this xpa",
+ "undefined command for this xpa",
+ "missing command for this xpa",
+ "name does not match target template",
+ "can't create data channel socket",
+ "could not read data buf (possible timeout)",
+ "illegal command or command switch"
+};
+
+/* temp static time buffer */
+static char ctimebuf[SZ_LINE];
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPAProxyConnect
+ *
+ * Purpose: connect to a client (in proxy mode)
+ *
+ * Returns: 0 if successfull, otherwise -1
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#ifdef ANSI_FUNC
+static int
+XPAProxyConnect(XPA xpa, char *method,
+ unsigned int *rip, unsigned short *rport, char *rname)
+#else
+static int XPAProxyConnect(xpa, method, rip, rport, rname)
+ XPA xpa;
+ char *method;
+ unsigned int *rip;
+ unsigned short *rport;
+ char *rname;
+#endif
+{
+ int ofd;
+ int tries=0;
+ int keep_alive=1;
+ unsigned int ip;
+ unsigned short port;
+ struct sockaddr_in sock_in;
+#if HAVE_SYS_UN_H
+ struct sockaddr_un sock_un;
+#endif
+
+ FPRINTF((stderr, "%sXPAProxyConnect to %s\n", _sp, method));
+ /* initialize results */
+ if( rip ) *rip = 0;
+ if( rport ) *rport = 0;
+ if( rname ) *rname = '\0';
+
+ switch(XPAMethod(method)){
+ case XPA_INET:
+again1:
+ if( !XPAParseIpPort(method, &ip, &port) ){
+ goto error;
+ }
+ /* use $localhost over $host (we don't trust host to be correct) */
+ if( (ip == gethostip("$host")) && (tries == 0) ){
+ ip = gethostip("$localhost");
+ }
+ /* connect to the server before we go further */
+ if( (ofd = xsocket(AF_INET, SOCK_STREAM, 0)) < 0 ){
+ PERROR(("XPAProxyConnect: socket()"));
+ goto error;
+ }
+ setsockopt(ofd, SOL_SOCKET, SO_KEEPALIVE,
+ (char *)&keep_alive, sizeof(keep_alive));
+ memset((char *)&sock_in, 0, sizeof(sock_in));
+ sock_in.sin_family = AF_INET;
+ sock_in.sin_addr.s_addr = htonl(ip);
+ sock_in.sin_port = htons(port);
+ /* make the connection with the server */
+ if(connect(ofd, (struct sockaddr *)&sock_in, sizeof(sock_in))<0){
+ xclose(ofd);
+ /* if localhost doesn't work, make one try with the host ip */
+ /* we also try again just in case there was an odd error such
+ as "permission denied", which we have seen once or twice */
+ if( tries < 2 ){
+ tries++;
+ goto again1;
+ }
+ /* give up */
+ else{
+ PERROR(("XPAProxyConnect: connect()"));
+ goto error;
+ }
+ }
+ break;
+#if HAVE_SYS_UN_H
+ case XPA_UNIX:
+again2:
+ ip = 0;
+ port = 0;
+ /* open a socket and fill in socket information */
+ if( (ofd = xsocket(AF_UNIX, SOCK_STREAM, 0)) < 0 ){
+ goto error;
+ }
+ setsockopt(ofd, SOL_SOCKET, SO_KEEPALIVE,
+ (char *)&keep_alive, sizeof(keep_alive));
+ memset((char *)&sock_un, 0, sizeof(sock_un));
+ sock_un.sun_family = AF_UNIX;
+ strcpy(sock_un.sun_path, method);
+ /* make the connection with the server */
+ if(connect(ofd, (struct sockaddr *)&sock_un, sizeof(sock_un))<0){
+ xclose(ofd);
+ /* Unix sockets get ECONNREFUSED when the listen queue is full,
+ so we try a few times to give the server a chance to recover */
+ if( (xerrno == ECONNREFUSED) && (tries < retries) ){
+ tries++;
+ XPASleep(10);
+ goto again2;
+ }
+ /* give up */
+ else{
+ goto error;
+ }
+ }
+ break;
+#endif
+ default:
+ goto error;
+ }
+
+ /* fill in blansk */
+ if( rip ) *rip = ip;
+ if( rport ) *rport = port;
+ if( rname ){
+ strncpy(rname, method, SZ_LINE-1);
+ rname[SZ_LINE-1] = '\0';
+ }
+ return(ofd);
+
+error:
+ return -1;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: CommNew
+ *
+ * Purpose: allocate a new comm record and add to end of list
+ *
+ * Returns: allocated comm record
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#ifdef ANSI_FUNC
+static XPAComm
+CommNew (XPA xpa, int fd, unsigned int ip, int port, char *name, NS ns)
+#else
+static XPAComm CommNew(xpa, fd, ip, port, name, ns)
+ XPA xpa;
+ int fd;
+ unsigned int ip;
+ int port;
+ char *name;
+ NS ns;
+#endif
+{
+ XPAComm comm, cur;
+ int i;
+ socklen_t slen;
+ struct sockaddr_in sock_in;
+#if HAVE_SYS_UN_H
+ struct sockaddr_un sock_un;
+#endif
+
+ /* create the comm struct */
+ if( (comm = (XPAComm)xcalloc(1, sizeof(XPACommRec))) == NULL )
+ return(NULL);
+
+ /* if fd < 0, we accept a connection to get the fd */
+ if( fd < 0 ){
+ /* accept the connection */
+ switch(mtype){
+ case XPA_INET:
+ while( 1 ){
+ slen = sizeof(struct sockaddr_in);
+ if((comm->cmdfd=xaccept(xpa->fd,(struct sockaddr *)&sock_in,&slen))>=0){
+ comm->cmdip = ntohl(sock_in.sin_addr.s_addr);
+ comm->cmdport = ntohs(sock_in.sin_port);
+ break;
+ }
+ else{
+ if( xerrno == EINTR )
+ continue;
+ else{
+ xfree(comm);
+ return(NULL);
+ }
+ }
+ }
+ break;
+#if HAVE_SYS_UN_H
+ case XPA_UNIX:
+ while( 1 ){
+ slen = sizeof(struct sockaddr_un);
+ if((comm->cmdfd=xaccept(xpa->fd,(struct sockaddr *)&sock_un,&slen))>=0){
+ comm->cmdname = xstrdup(sock_un.sun_path);
+ break;
+ }
+ else{
+ if( xerrno == EINTR )
+ continue;
+ else{
+ xfree(comm);
+ return(NULL);
+ }
+ }
+ }
+ break;
+#endif
+ default:
+ xfree(comm);
+ return(NULL);
+ }
+ }
+ /* all info is supplied */
+ else{
+ switch(mtype){
+ case XPA_INET:
+ comm->cmdip = ip;
+ comm->cmdport = port;
+ break;
+ case XPA_UNIX:
+ comm->cmdname = xstrdup(name);
+ break;
+ }
+ comm->cmdfd = fd;
+ /* store name server record */
+ comm->ns = ns;
+ }
+
+ /* set back pointer */
+ /* make sure we close on exec */
+ xfcntl(comm->cmdfd, F_SETFD, FD_CLOEXEC);
+ /* mark data socket with impossible value */
+ comm->datafd = -1;
+ /* default is to ack */
+ comm->ack = 1;
+ /* set default cendian flag */
+ comm->cendian = "?";
+ /* clear the acl flags */
+ for(i=0; i<XPA_CMDS+1; i++){
+ comm->acl[i] = -1;
+ }
+
+ /* add this comm to end of list of comms */
+ if( xpa->commhead == NULL ){
+ xpa->commhead = comm;
+ }
+ else{
+ for(cur=xpa->commhead; cur->next!=NULL; cur=cur->next){
+ ;
+ }
+ cur->next = comm;
+ }
+
+ /* we might have to add this fd specially to a non-select event loop */
+ if( xpa->seladd )
+ comm->selcptr = (xpa->seladd)(xpa, comm->cmdfd);
+
+ /* make this fd active */
+ XPAActive(xpa, comm, 1);
+
+ FPRINTF((stderr, "%sCommNew: ip=%x port=%d fd=%d\n", _sp,
+ comm->cmdip, comm->cmdport, comm->cmdfd));
+
+ /* return the good news */
+ return(comm);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: CommFree
+ *
+ * Purpose: free a comm record and remove from list
+ *
+ * Returns: none
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+static void
+CommFree (XPA xpa, XPAComm comm, int flag)
+#else
+static void CommFree(xpa, comm, flag)
+ XPA xpa;
+ XPAComm comm;
+ int flag;
+#endif
+{
+ XPAComm cur;
+
+ if( !comm )
+ return;
+
+ FPRINTF((stderr, "%sCommFree: ip=%x port=%d fd=%d dfd=%d\n", _sp,
+ comm->cmdip, comm->cmdport, comm->cmdfd, comm->datafd));
+ /* remove from list of this xpa's comms */
+ if( xpa ){
+ if( xpa->commhead ){
+ if( xpa->commhead == comm ){
+ xpa->commhead = comm->next;
+ }
+ else{
+ for(cur=xpa->commhead; cur!=NULL; cur=cur->next){
+ if( cur->next == comm ){
+ cur->next = comm->next;
+ break;
+ }
+ }
+ }
+ }
+ }
+ /* must check all xpas */
+ else{
+ for(xpa=xpahead; xpa!=NULL; xpa=xpa->next){
+ if( xpa->commhead ){
+ if( xpa->commhead == comm ){
+ xpa->commhead = comm->next;
+ }
+ else{
+ for(cur=xpa->commhead; cur!=NULL; cur=cur->next){
+ if( cur->next == comm ){
+ cur->next = comm->next;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* close socket connections */
+ if( flag && (comm->cmdfd >= 0) ){
+ FPRINTF((stderr, "%sCommFree closing cmd fd: %d\n", _sp, comm->cmdfd));
+ /* remove from active */
+ if( comm->cmdfd < FD_SETSIZE )
+ activefds[comm->cmdfd] = 0;
+ /* delete the comm cmd fd specially from a non-select event loop */
+ if( xpa && xpa->seldel && comm->selcptr ){
+ (xpa->seldel)(comm->selcptr);
+ comm->selcptr = NULL;
+ }
+ /* close file */
+ xclose(comm->cmdfd);
+ }
+ /* close data channel */
+ XPACloseData(xpa, comm);
+ /* if we have a file name (unix sockets), free it */
+ if( comm->cmdname != NULL ){
+ unlink(comm->cmdname);
+ xfree(comm->cmdname);
+ }
+ if( comm->dataname != NULL ){
+ unlink(comm->dataname);
+ xfree(comm->dataname);
+ }
+
+ /* free up the space */
+ if( comm->id != NULL ) xfree(comm->id);
+ if( comm->info != NULL ) xfree(comm->info);
+ if( comm->target != NULL ) xfree(comm->target);
+ if( comm->paramlist != NULL ) xfree(comm->paramlist);
+
+ /* this comm is no longer associated with an ns */
+ if( comm->ns ){
+ comm->ns->nproxy -= 1;
+ }
+
+ /* disassociate from parent xpa */
+ if( xpa ){
+ xpa->comm = NULL;
+ }
+
+ /* free up structure */
+ xfree(comm);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPANSOpen
+ *
+ * Purpose: open a connection to the name service
+ *
+ * Returns: connection fd on success, -1 on failure
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+static NS
+XPANSOpen (XPA xpa, char *host, int force)
+#else
+static NS XPANSOpen(xpa, host, force)
+ XPA xpa;
+ char *host;
+ int force;
+#endif
+{
+ int i;
+ int status;
+ int xnsfd=0;
+ int keep_alive=1;
+ int tries=0;
+ int ip=0;
+ int dowarn=0;
+ int contmode=XPA_CONNECT_TIMEOUT_MODE;
+ unsigned short xnsport=0;
+ unsigned int xnsip=0;
+ static int errinit=0;
+ char *s;
+ char *path;
+ char *method;
+ char nscmd[SZ_LINE];
+ char tbuf[SZ_LINE];
+ char tbuf2[SZ_LINE];
+ struct sockaddr_in sock_in;
+ struct passwd *pw;
+#if HAVE_SYS_UN_H
+ struct sockaddr_un sock_un;
+#endif
+ char *findname=NULL;
+ NS ns, cur;
+
+ /* get name server method */
+ method = XPANSMethod(host, 0);
+
+ /* if the name service already is open, just return fd */
+ if( xpa ){
+ for(ns=xpa->nshead; ns!=NULL; ns=ns->next){
+ if( !strcmp(ns->method, method) ){
+ /* if forcing, see if the connection is valid */
+ if( force >= 0 ){
+ if( (XPAPuts(xpa, ns->fd, "status\n", stimeout) >0) &&
+ (XPAGets(xpa, ns->fd, tbuf, SZ_LINE, stimeout) >0) &&
+ !strncmp(tbuf, "XPA$OK", 6) ){
+ FPRINTF((stderr, "%sXPANSOpen: found existing ns: %s\n",
+ _sp, ns->method));
+ return ns;
+ }
+ /* found the ns, but its not open */
+ else{
+ FPRINTF((stderr, "%sXPANSOpen: existing ns is dead: %s\n",
+ _sp, ns->method));
+ XPANSClose(xpa, ns);
+ break;
+ }
+ }
+ /* just return whatever we have if we are not forcing */
+ else{
+ return ns;
+ }
+ }
+ }
+ }
+
+ /* if no existing ns and flag is negative, we are done */
+ if( force == -1 )
+ return(NULL);
+
+ /* we always make up the command afresh */
+ *nscmd = '\0';
+
+ /* get users for this user */
+ if( (s=(char *)getenv("XPA_NSUSERS")) != NULL )
+ strncpy(nsusers, s, SZ_LINE-1);
+ /* default is just this use's userrname, from the environment */
+ else if( (s=(char *)getenv("LOGNAME")) != NULL )
+ strncpy(nsusers, s, SZ_LINE-1);
+#if HAVE_MINGW32==0
+ /* this is a last resort */
+ else if( (pw=getpwuid(geteuid())) )
+ strncpy(nsusers, pw->pw_name, SZ_LINE-1);
+#endif
+ /* if nothing good has happened, make it "all" */
+ if( *nsusers == '\0' )
+ strcpy(nsusers, "*");
+ /* null-terminate string */
+ nsusers[SZ_LINE-1] = '\0';
+
+ /* set up communications socket for this method */
+ switch(mtype){
+ case XPA_INET:
+again1:
+ XPAParseIpPort(method, &xnsip, &xnsport);
+ /* use $localhost over $host (we do not trust host to be correct) */
+ if( (xnsip == gethostip("$host")) && (tries == 0) ){
+ xnsip = gethostip("$localhost");
+ }
+ if( xnsip == 0 ){
+ fprintf(stderr,
+ "XPA$ERROR: invalid host name specified: %s.\n", method);
+ return(NULL);
+ }
+ if( (xnsfd = xsocket(AF_INET, SOCK_STREAM, 0)) < 0 )
+ goto nons;
+ memset((char *)&sock_in, 0, sizeof(sock_in));
+ sock_in.sin_family = AF_INET;
+ sock_in.sin_addr.s_addr = htonl(xnsip);
+ sock_in.sin_port = htons(xnsport);
+ /* try connecting to the name server */
+ if( ctimeout <= 0 ) contmode = 0;
+ switch(contmode){
+ case 1:
+#if HAVE_MINGW32==0
+ status=alrmconnect(xnsfd, (void *)&sock_in, sizeof(sock_in), ctimeout);
+#else
+ status=noblkconnect(xnsfd, (void *)&sock_in, sizeof(sock_in), ctimeout);
+#endif
+ break;
+ case 2:
+ status=noblkconnect(xnsfd, (void *)&sock_in, sizeof(sock_in), ctimeout);
+ break;
+ default:
+ status=connect(xnsfd, (struct sockaddr *)&sock_in, sizeof(sock_in));
+ break;
+ }
+ if( status == 0 ){
+ FPRINTF((stderr, "%sXPANSOpen: connect to xpans\n", _sp));
+ goto okns;
+ }
+ else{
+ xclose(xnsfd);
+ /* if localhost doesn't work, make one try with the host ip */
+ if( (xerrno == ECONNREFUSED) && (tries < 1) ){
+ tries++;
+ goto again1;
+ }
+ }
+ break;
+#if HAVE_SYS_UN_H
+ case XPA_UNIX:
+again2:
+ /* open a socket and fill in socket information */
+ if( (xnsfd = xsocket(AF_UNIX, SOCK_STREAM, 0)) < 0 )
+ goto nons;
+ memset((char *)&sock_un, 0, sizeof(sock_un));
+ sock_un.sun_family = AF_UNIX;
+ strcpy(sock_un.sun_path, method);
+ xnsip = 0;
+ /* try connecting to the name server */
+ status=connect(xnsfd, (struct sockaddr *)&sock_un, sizeof(sock_un));
+ if( status == 0 ){
+ FPRINTF((stderr, "%sXPANSOpen: connect to xpans\n", _sp));
+ goto okns;
+ }
+ else{
+ xclose(xnsfd);
+ /* Unix sockets get ECONNREFUSED when the listen queue is full,
+ so we try a few times to give the server a chance to recover */
+ if( (xerrno == ECONNREFUSED) && (tries < retries) ){
+ tries++;
+ XPASleep(10);
+ goto again2;
+ }
+ }
+ break;
+#endif
+ }
+
+ /* if force is set, we try to start up the name server */
+ if( force == 0 )
+ goto noforce;
+
+ /* make up the xpans command we will use */
+ if( *nscmd == '\0' ){
+ if( (mtype == XPA_UNIX) || LOCALIP(xnsip) ){
+ path = (char *)getenv("PATH");
+ findname = (char *)Find(XPANSNAME, "x", NULL, path);
+#if HAVE_CYGWIN
+ /* this will help start up xpans under Windows */
+ if( !findname ) findname = (char *)Find(XPANSNAME, "x", NULL, ".");
+#endif
+ }
+ if( findname != NULL ){
+ switch(mtype){
+ case XPA_INET:
+#if USE_LAUNCH
+ /* change spaces to special launch space */
+ for(i=0; i<strlen(findname); i++){
+ if( findname[i] == ' '){
+ findname[i] = LAUNCH_SPACE;
+ }
+ }
+ snprintf(nscmd, SZ_LINE, "%s -e -p %d -l %s/xpans_%d.log",
+ findname, xnsport, tmpdir, xnsport);
+#else
+ snprintf(nscmd, SZ_LINE, "\"%s\" -e -p %d -l %s/xpans_%d.log &\n",
+ findname, xnsport, tmpdir, xnsport);
+#endif
+ break;
+#if HAVE_SYS_UN_H
+ case XPA_UNIX:
+#if USE_LAUNCH
+ /* change spaces to special launch space */
+ for(i=0; i<strlen(findname); i++){
+ if( findname[i] == ' '){
+ findname[i] = LAUNCH_SPACE;
+ }
+ }
+ snprintf(nscmd, SZ_LINE, "%s -e -f %s -l %s.log",
+ findname, method, method);
+#else
+ snprintf(nscmd, SZ_LINE, "\"%s\" -e -f %s -l %s.log &\n",
+ findname, method, method);
+#endif
+ break;
+#endif
+ }
+ }
+ else{
+ *nscmd = '\0';
+ }
+ }
+
+ /* did not find the name server -- start it up if we can, i.e.,
+ if its on the same machine as we are on and we found it in the path */
+ if((*nscmd != '\0') && ((mtype == XPA_UNIX) || LOCALIP(xnsip)) ){
+ FPRINTF((stderr, "%sLaunching: %s\n", _sp, nscmd));
+#if USE_LAUNCH
+ if( Launch(nscmd, 0, NULL, NULL) != 0 )
+ goto nons;
+#else
+ if( system(nscmd) != 0 )
+ goto nons;
+#endif
+ /* enter loop looking to connect */
+ for(tries=0; tries<retries; tries++){
+ switch(mtype){
+ case XPA_INET:
+ /* use $localhost alternately with $host */
+ if( (xnsip == gethostip("$host")) && (tries % 2 == 0) ){
+ xnsip = gethostip("$localhost");
+ }
+ if( xnsip == 0 ){
+ fprintf(stderr,
+ "XPA$ERROR: invalid host name specified: %s.\n", method);
+ return(NULL);
+ }
+ if( (xnsfd = xsocket(AF_INET, SOCK_STREAM, 0)) < 0 )
+ goto nons;
+ memset((char *)&sock_in, 0, sizeof(sock_in));
+ sock_in.sin_family = AF_INET;
+ sock_in.sin_addr.s_addr = htonl(xnsip);
+ sock_in.sin_port = htons(xnsport);
+ FPRINTF((stderr, "%sXPANSOPEN: attempting connect to %x\n",
+ _sp, xnsip));
+ if(connect(xnsfd, (struct sockaddr *)&sock_in, sizeof(sock_in)) ==0)
+ goto okns;
+ break;
+#if HAVE_SYS_UN_H
+ case XPA_UNIX:
+ if( (xnsfd = xsocket(AF_UNIX, SOCK_STREAM, 0)) < 0 )
+ goto nons;
+ if(connect(xnsfd, (struct sockaddr *)&sock_un, sizeof(sock_un)) ==0)
+ goto okns;
+ break;
+#endif
+ }
+ xclose(xnsfd);
+ XPASleep(nsdelay);
+ }
+ /* if we got here, we did not connect */
+ goto nons;
+ }
+ /* name server is on a remote machine, so there is no hope */
+ else{
+ goto nons;
+ }
+
+okns:
+ /* make sure we close on exec */
+ xfcntl(xnsfd, F_SETFD, FD_CLOEXEC);
+ setsockopt(xnsfd, SOL_SOCKET, SO_KEEPALIVE,
+ (char *)&keep_alive, sizeof(keep_alive));
+ /* fill in new record */
+ if( (ns = (NS)xcalloc(1, sizeof(NSRec))) == NULL ){
+ xclose(xnsfd);
+ return NULL;
+ }
+ ns->method = xstrdup(method);
+ ns->host = xstrdup(host);
+ ns->fd = xnsfd;
+ FPRINTF((stderr, "%sXPANSOpen: host %s opened on fd %d\n", _sp,
+ host?host:"<unknown>", xnsfd));
+ switch(mtype){
+ case XPA_INET:
+ ns->ip = xnsip;
+ ns->port = xnsport;
+ break;
+#if HAVE_SYS_UN_H
+ case XPA_UNIX:
+ ns->name = xstrdup(method);
+ break;
+#endif
+ }
+ if( xpa ){
+ /* add to list of name servers */
+ if( xpa->nshead == NULL ){
+ xpa->nshead = ns;
+ }
+ else{
+ for(cur=xpa->nshead; cur->next!=NULL; cur=cur->next)
+ ;
+ cur->next = ns;
+ }
+ }
+ /* check version with xpans */
+ snprintf(tbuf, SZ_LINE, "version %s\n", XPA_VERSION);
+ if( (XPAPuts(xpa, ns->fd, tbuf, stimeout) >0) &&
+ (XPAGets(xpa, ns->fd, tbuf, SZ_LINE, stimeout) >0) ){
+ if( word(tbuf, tbuf2, &ip) ){
+ if( !strcmp(tbuf2, "XPA$VERSION") ){
+ if( word(tbuf, tbuf2, &ip) ){
+ /* version check: our server version should be <= xpans version */
+ dowarn = (XPAVersionCheck(XPA_VERSION, tbuf2)>0);
+ }
+ else{
+ strcpy(tbuf2, "unknown/pre-2.1 (noversion)");
+ dowarn = 1;
+ }
+ }
+ else{
+ strcpy(tbuf2, "unknown/pre-2.1 (badformat)");
+ dowarn = 1;
+ }
+ }
+ FPRINTF((stderr, "%sXPANSOpen: version info: %s\n", _sp, tbuf));
+ if( dowarn ){
+ XPAVersionWarn(XPA_VERSION, tbuf2);
+ }
+ }
+
+ /* clean up */
+ if( findname != NULL ) xfree(findname);
+ return(ns);
+
+nons:
+ /* if we specified an explicit port, we don't need the name server,
+ so don't bother with any warning or error messages */
+ if( XPAPort(xpa) >0 )
+ return NULL;
+ switch(mtype){
+ case XPA_INET:
+ if( !errinit && verbosity ){
+ if( LOCALIP(xnsip) ){
+ fprintf(stderr,
+ "XPA$WARNING: xpans needs to be running on this machine.\n");
+ }
+ else{
+ fprintf(stderr,
+ "XPA$WARNING: xpans needs to be running on machine: ");
+ fprintf(stderr, "%s\n", getiphost(xnsip));
+ }
+ }
+ break;
+#if HAVE_SYS_UN_H
+ case XPA_UNIX:
+ if( !errinit && verbosity ){
+ fprintf(stderr,
+ "XPA$WARNING: xpans needs to be running on this machine.\n");
+ }
+ break;
+#endif
+ default:
+ break;
+ }
+ if( xpa && verbosity ){
+ if( !errinit ){
+ /* make up the command users will need to start xpans */
+ if( *nscmd == '\0' ){
+ switch(mtype){
+ case XPA_INET:
+ snprintf(nscmd, SZ_LINE, "xpans -e -p %d -l %s/xpans_%d.log",
+ xnsport, tmpdir, xnsport);
+ break;
+#if HAVE_SYS_UN_H
+ case XPA_UNIX:
+ snprintf(nscmd, SZ_LINE, "xpans -e -f %s -l %s.log", method, method);
+ break;
+#endif
+ }
+ }
+ fprintf(stderr, "Please start xpans using the command:\n\t%s\n", nscmd);
+ fprintf(stderr, "Once xpans is running, register all xpas in this process using:\n");
+ fprintf(stderr, "\txpaset -p %s -nsconnect\n", xpa->method);
+ }
+ fprintf(stderr, "For now, contact %s using:", xpa->name);
+ fprintf(stderr, " xpaset %s .. or xpaget %s ..\n",
+ xpa->method, xpa->method);
+ }
+ errinit++;
+
+noforce:
+ if( findname != NULL ) xfree(findname);
+ return(NULL);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Routine: _XPAFree
+ *
+ * Purpose: free up memory in the XPA record structure
+ * (internal version)
+ *
+ * Results: 0 on success, -1 for failure
+ *
+ *---------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+static int
+_XPAFree (XPA xpa)
+#else
+static int _XPAFree(xpa)
+ XPA xpa;
+#endif
+{
+ char tbuf[SZ_LINE];
+ XPACmd cmd, tcmd;
+ XPAComm comm, tcomm;
+ XPAClip clip, tclip;
+ NS ns, tns;
+
+ /* make sure we have something to do */
+ if( xpa == NULL )
+ return(-1);
+
+ FPRINTF((stderr, "%s_XPAFree: freeing xpa struct\n", _sp));
+ /* remove this xpa from public availability */
+ if( xpa->type != NULL )
+ XPANSDel(xpa, NULL, NULL);
+
+ /* free all sub-commands */
+ for(cmd=xpa->commands; cmd!=NULL; ){
+ tcmd = cmd->next;
+ XPACmdDel(xpa, cmd);
+ cmd = tcmd;
+ }
+
+ /* remove from list of xpas */
+ XPAListDel(&xpahead, xpa);
+
+ /* close down listening socket */
+ if( xpa->fd >= 0 )
+ xclose(xpa->fd);
+
+ /* perform method-specific cleanup */
+ switch(mtype){
+#if HAVE_SYS_UN_H
+ case XPA_UNIX:
+ /* delete the unix socket files */
+ unlink(xpa->method);
+ snprintf(tbuf, SZ_LINE, "%s_data", xpa->method);
+ unlink(tbuf);
+ break;
+#endif
+ default:
+ break;
+ }
+
+ /* free up space */
+ if( xpa->version ) xfree(xpa->version);
+ if( xpa->type ) xfree(xpa->type);
+ if( xpa->method ) xfree(xpa->method);
+ if( xpa->xclass ) xfree(xpa->xclass);
+ if( xpa->name ) xfree(xpa->name);
+ if( xpa->help ) xfree(xpa->help);
+ if( xpa->sendian ) xfree(xpa->sendian);
+
+ /* call the select free routine for the listening socket and loop type.
+ we use an indirect routine to avoid having to link Xt, Tcl, etc. */
+ if( xpa->seldel && xpa->selptr ){
+ (xpa->seldel)(xpa->selptr);
+ xpa->selptr = NULL;
+ }
+
+ /* close communication channels */
+ for(comm=xpa->commhead; comm!=NULL; ){
+ tcomm = comm->next;
+ CommFree(xpa, comm, 1);
+ comm = tcomm;
+ }
+
+ /* free up clipboards */
+ for(clip=xpa->cliphead; clip!=NULL; ){
+ tclip = clip->next;
+ ClipBoardFree(xpa, clip);
+ clip = tclip;
+ }
+
+ /* close down the name server and all of the remotes for this xpa */
+ for(ns=xpa->nshead; ns!=NULL; ){
+ tns = ns->next;
+ XPANSClose(xpa, ns);
+ ns = tns;
+ }
+
+ /* free up record struct */
+ xfree((char *)xpa);
+
+ return(0);
+}
+
+#if HAVE_ATEXIT
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Routine: _XPAAtExit
+ *
+ * Purpose: XPA cleanup on exit
+ * The main purpose of this routine is to try to delete the
+ * unix socket files when an XPA server exits.
+ *
+ * Results: none
+ *
+ *---------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+static void
+_XPAAtExit (void)
+#else
+static void _XPAAtExit()
+#endif
+{
+ XPA xpa, txpa;
+ static int done=0;
+
+ if( !done ){
+ /* return if we were not initialized */
+ if( !atexitinit ) return;
+ /* return if I am not the process who initialized (I'm a child) */
+ if( atexitinit != getpid() ) return;
+ FPRINTF((stderr, "calling XPAAtExit\n"));
+ for(xpa=xpahead; xpa!=NULL; ){
+ /* use temp in case we destroy structure in the cleanup */
+ txpa = xpa->next;
+ _XPAFree(xpa);
+ xpa = txpa;
+ }
+ /* platform-dependent cleanup */
+ xsocketcleanup();
+ /* done with cleanup */
+ done++;
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Routine: XPAAtExit
+ *
+ * Purpose: set up XPA cleanup on exit
+ *
+ * Results: none
+ *
+ *---------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+void
+XPAAtExit (void)
+#else
+void XPAAtExit()
+#endif
+{
+ if( !atexitinit ){
+ atexit(_XPAAtExit);
+ atexitinit = getpid();
+ }
+}
+
+#endif
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ *
+ * Semi-Public Routines
+ * (mainly used by xpaset and xpaget)
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Routine: _XPAValid
+ *
+ * Purpose: see if the xpa struct is valid
+ *
+ * Results: 1 on success, 0 for failure
+ *
+ *---------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+_XPAValid (XPA head, XPA xpa, char *type)
+#else
+int _XPAValid(head, xpa, type)
+ XPA head;
+ XPA xpa;
+ char *type;
+#endif
+{
+ XPA cur;
+
+ if( xpa == NULL )
+ return(0);
+ for(cur=head; cur!=NULL; cur=cur->next){
+ if( (cur == xpa) && !strcspn(cur->type, type) ){
+ return(1);
+ }
+ }
+ return(0);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Routine: XPAValid
+ *
+ * Purpose: see if the xpa struct is valid
+ *
+ * Results: 1 on success, 0 for failure
+ *
+ *---------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPAValid (XPA xpa)
+#else
+int XPAValid(xpa)
+ XPA xpa;
+#endif
+{
+ if( _XPAValid(xpahead, xpa, XPA_ACLS) )
+ return(1);
+ else
+ return(0);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPAEndian
+ *
+ * Purpose: semi-public routine to return the endian-ness of this
+ * machine
+ *
+ * Results: 0 if little endian, 1 if bigendian
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPAEndian(void)
+#else
+int XPAEndian()
+#endif
+{
+ union
+ {
+ long l;
+ char c[sizeof (long)];
+ } u;
+ u.l = 1;
+ return(u.c[sizeof (long) - 1] == 1);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPAListHead
+ *
+ * Purpose: semi-public routine to return the head of the xpa list
+ *
+ * Results: XPA list pointer on success
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+XPA
+XPAListHead (void)
+#else
+XPA XPAListHead()
+#endif
+{
+ return(xpahead);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Routine: XPAListAdd
+ *
+ * Purpose: add a member of an xpa list
+ *
+ * Results: 1 on success, 0 for failure
+ *
+ *---------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+void
+XPAListAdd (XPA *head, XPA xpa)
+#else
+void XPAListAdd(head, xpa)
+ XPA *head;
+ XPA xpa;
+#endif
+{
+ XPA cur;
+
+ if( *head == NULL ){
+ *head = xpa;
+ }
+ else{
+ for(cur=*head; cur->next!=NULL; cur=cur->next)
+ ;
+ cur->next = xpa;
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Routine: XPAListDel
+ *
+ * Purpose: remove a member of an xpa list
+ *
+ * Results: 1 on success, 0 for failure
+ *
+ *---------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+void
+XPAListDel (XPA *head, XPA xpa)
+#else
+void XPAListDel(head, xpa)
+ XPA *head;
+ XPA xpa;
+#endif
+{
+ XPA cur;
+
+ /* remove from list of xpas */
+ if( *head ){
+ if( *head == xpa ){
+ *head = xpa->next;
+ }
+ else{
+ for(cur=*head; cur!=NULL; cur=cur->next){
+ if( cur->next == xpa ){
+ cur->next = xpa->next;
+ break;
+ }
+ }
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPAActive
+ *
+ * Purpose: make the xpa, cmd and data fds active or inactive for select
+ *
+ * Returns: none
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPAActive (XPA xpa, XPAComm comm, int flag)
+#else
+int XPAActive(xpa, comm, flag)
+ XPA xpa;
+ XPAComm comm;
+ int flag;
+#endif
+{
+ int prev=0;
+
+ /* sanity check */
+ if( !xpa ) return(0);
+
+ switch(flag){
+ /* remove this xpa from the active list */
+ case 0:
+ if( (xpa->fd >= 0) && (xpa->fd < FD_SETSIZE) ){
+ FPRINTF((stderr, "%sXPAActive: clearing fd %d\n", _sp, xpa->fd));
+ prev = activefds[xpa->fd];
+ activefds[xpa->fd] = 0;
+ if( xpa->seloff && xpa->selptr )
+ (xpa->seloff)(xpa->selptr);
+ }
+ if( comm ){
+ if( (comm->cmdfd >= 0) && (comm->cmdfd < FD_SETSIZE) ){
+ activefds[comm->cmdfd] = 0;
+ if( xpa->seloff && comm->selcptr )
+ (xpa->seloff)(comm->selcptr);
+ }
+ if( (comm->datafd >= 0) && (comm->datafd < FD_SETSIZE) ){
+ activefds[comm->datafd] = 0;
+ if( xpa->seloff && comm->seldptr )
+ (xpa->seloff)(comm->seldptr);
+ }
+ }
+ break;
+ /* add this xpa/comm to the active list */
+ case 1:
+ if( (xpa->fd >= 0) && (xpa->fd < FD_SETSIZE) ){
+ FPRINTF((stderr, "%sXPAActive: activating fd %d\n", _sp, xpa->fd));
+ prev = activefds[xpa->fd];
+ activefds[xpa->fd] = 1;
+ if( xpa->selon && xpa->selptr )
+ (xpa->selon)(xpa->selptr);
+ }
+ if( comm ){
+ if( (comm->cmdfd >= 0) && (comm->cmdfd < FD_SETSIZE) ){
+ activefds[comm->cmdfd] = 1;
+ if( xpa->selon && comm->selcptr )
+ (xpa->selon)(comm->selcptr);
+ }
+ if( (comm->datafd >= 0) && (comm->datafd < FD_SETSIZE) ){
+ activefds[comm->datafd] = 1;
+ if( xpa->selon && comm->seldptr )
+ (xpa->selon)(comm->seldptr);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return(prev);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPAActiveFd
+ *
+ * Purpose: semi-public routine to return flag if fd is active
+ *
+ * Results: 1 is active, 0 is inactive
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPAActiveFd (int fd)
+#else
+int XPAActiveFd(fd)
+ int fd;
+#endif
+{
+ if( (fd >= 0) && (fd < FD_SETSIZE) && (activefds[fd] > 0) )
+ return(1);
+ else
+ return(0);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPAAddSelect
+ *
+ * Purpose: add one or more xpa sockets to the select flags
+ *
+ * Return: number of xpas that were added to the select flags
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPAAddSelect (XPA xpa, fd_set *readfdsptr)
+#else
+int XPAAddSelect(xpa, readfdsptr)
+ XPA xpa;
+ fd_set *readfdsptr;
+#endif
+{
+ XPA cur;
+ XPAComm comm;
+ int got=0;
+
+ /* better have some place to set the flags */
+ if( readfdsptr == NULL )
+ return(0);
+
+ /* if a specific xpa was specified, just set its select flags */
+ if( xpa != NULL ){
+ if( XPAActiveFd(xpa->fd) ){
+ FD_SET(xpa->fd, readfdsptr);
+ got++;
+ /* note that we only select on coms if the main fd is active */
+ for(comm=xpa->commhead; comm!=NULL; comm=comm->next){
+ if( XPAActiveFd(comm->cmdfd) ){
+ FD_SET(comm->cmdfd, readfdsptr);
+ got++;
+ }
+ if( XPAActiveFd(comm->datafd) && (comm->datafd != comm->cmdfd) ){
+ FD_SET(comm->datafd, readfdsptr);
+ got++;
+ }
+ }
+ }
+ }
+ /* otherwise set select flags for all xpas */
+ else{
+ for(cur=xpahead; cur!=NULL; cur=cur->next){
+ if( XPAActiveFd(cur->fd) ){
+ FPRINTF((stderr, "%sXPAAddSelect: adding fd %d\n", _sp, cur->fd));
+ FD_SET(cur->fd, readfdsptr);
+ got++;
+ /* note that we only select on coms if the main fd is active */
+ for(comm=cur->commhead; comm!=NULL; comm=comm->next){
+ if( XPAActiveFd(comm->cmdfd) ){
+ FPRINTF((stderr, "%sXPAAddSelect: adding cmdfd %d\n",
+ _sp, comm->cmdfd));
+ FD_SET(comm->cmdfd, readfdsptr);
+ got++;
+ }
+ if( XPAActiveFd(comm->datafd) && (comm->datafd != comm->cmdfd) ){
+ FPRINTF((stderr, "%sXPAAddSelect: adding datafd %d\n",
+ _sp, comm->datafd));
+ FD_SET(comm->datafd, readfdsptr);
+ got++;
+ }
+ }
+ }
+ }
+ }
+ return(got);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPAProcessSelect
+ *
+ * Purpose: process xpas that have pending reads or writes
+ *
+ * Return: number of xpas processed
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPAProcessSelect (fd_set *readfdsptr, int maxreq)
+#else
+int XPAProcessSelect(readfdsptr, maxreq)
+ fd_set *readfdsptr;
+ int maxreq;
+#endif
+{
+ int got=0;
+ XPA xpa;
+ XPAComm comm;
+#ifdef OLD
+ XPA txpa;
+ XPAComm tcomm;
+#endif
+
+ /* <= 0 means do all of them */
+ if( maxreq < 0 ){
+ maxreq = 0;
+ }
+
+again:
+ for(xpa=xpahead; xpa!=NULL; xpa=xpa->next){
+ /* handle command requests */
+ for(comm=xpa->commhead; comm!=NULL; comm=comm->next){
+ if( (comm->cmdfd >=0) && FD_ISSET(comm->cmdfd, readfdsptr) ){
+ FD_CLR(comm->cmdfd, readfdsptr);
+ XPAHandler(xpa, comm->cmdfd);
+ got++;
+ if( maxreq && (got >= maxreq) ) return(got);
+ goto again;
+ }
+ }
+ /* handle data requests */
+ for(comm=xpa->commhead; comm!=NULL; comm=comm->next){
+ if( (comm->datafd >=0) && FD_ISSET(comm->datafd, readfdsptr) ){
+ FD_CLR(comm->datafd, readfdsptr);
+ XPAHandler(xpa, comm->datafd);
+ got++;
+ if( maxreq && (got >= maxreq) ) return(got);
+ goto again;
+ }
+ }
+ /* handle new requests */
+ if( (xpa->fd >= 0) && FD_ISSET(xpa->fd, readfdsptr) ){
+ FD_CLR(xpa->fd, readfdsptr);
+ XPAHandler(xpa, xpa->fd);
+ got++;
+ if( maxreq && (got >= maxreq) ) return(got);
+ goto again;
+ }
+ }
+
+#ifdef OLD
+ for(xpa=xpahead; xpa!=NULL; ){
+ txpa = xpa->next;
+ /* handle command requests */
+ for(comm=xpa->commhead; comm!=NULL; ){
+ tcomm = comm->next;
+ if( (comm->cmdfd >=0) && FD_ISSET(comm->cmdfd, readfdsptr) ){
+ FD_CLR(comm->cmdfd, readfdsptr);
+ FPRINTF((stderr, "%sXPAProcessSelect: cmd %d\n", _sp, comm->cmdfd));
+ /* if we got an error on this xpa, skip processing rest of it */
+ if( XPAHandler(xpa, comm->cmdfd) != XPA_RTN_OK ){
+ goto nextxpa;
+ }
+ got++;
+ /* if we freed this xpa, skip processing rest of it */
+ if( !_XPAValid(xpahead, xpa, XPA_ACLS) ){
+ goto nextxpa;
+ }
+ /* if we are have processed the max reqests, we are done */
+ if( maxreq && (got >= maxreq) ){
+ return(got);
+ }
+ }
+ comm = tcomm;
+ }
+ /* handle data requests */
+ for(comm=xpa->commhead; comm!=NULL; ){
+ tcomm = comm->next;
+ if( (comm->datafd >=0) && FD_ISSET(comm->datafd, readfdsptr) ){
+ FD_CLR(comm->datafd, readfdsptr);
+ FPRINTF((stderr, "%sXPAProcessSelect: data %d\n", _sp, comm->datafd));
+ /* if we got an error on this xpa, skip processing rest of it */
+ if( XPAHandler(xpa, comm->datafd) != XPA_RTN_OK ){
+ goto nextxpa;
+ }
+ got++;
+ /* if we freed this xpa, skip processing rest of it */
+ if( !_XPAValid(xpahead, xpa, XPA_ACLS) ){
+ goto nextxpa;
+ }
+ /* if we are have processed the max reqests, we are done */
+ if( maxreq && (got >= maxreq) ){
+ return(got);
+ }
+ }
+ comm = tcomm;
+ }
+ /* handle new requests */
+ if( (xpa->fd >= 0) && FD_ISSET(xpa->fd, readfdsptr) ){
+ FD_CLR(xpa->fd, readfdsptr);
+ FPRINTF((stderr, "%sXPAProcessSelect: xpa %d\n", _sp, xpa->fd));
+ /* if we got an error on this xpa, skip processing rest of it */
+ if( XPAHandler(xpa, xpa->fd) != XPA_RTN_OK ){
+ goto nextxpa;
+ }
+ got++;
+ /* if we freed this xpa, skip processing rest of it */
+ if( !_XPAValid(xpahead, xpa, XPA_ACLS) ){
+ goto nextxpa;
+ }
+ /* if we are have processed the max reqests, we are done */
+ if( maxreq && (got >= maxreq) ){
+ return(got);
+ }
+ }
+
+nextxpa:
+ /* must check to see if last xpa freed the next xpa */
+ if( _XPAValid(xpahead, txpa, XPA_ACLS) )
+ xpa = txpa;
+ else
+ break;
+ }
+#endif
+ FPRINTF((stderr, "%sXPAProcessSelect: returns %d\n", _sp, got));
+ return(got);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPACloseData
+ *
+ * Purpose: close data fd if its not the cmd fd
+ *
+ * Returns: none
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+void
+XPACloseData (XPA xpa, XPAComm comm)
+#else
+void XPACloseData(xpa, comm)
+ XPA xpa;
+ XPAComm comm;
+#endif
+{
+ /* we close the data channel if its not the command channel */
+ if( comm && (comm->datafd >=0) ){
+ if( comm->cmdfd != comm->datafd ){
+ FPRINTF((stderr, "%sXPACloseData: close fd %d for cmd fd %d\n", _sp,
+ comm->datafd, comm->cmdfd));
+ /* remove from active */
+ if( comm->datafd < FD_SETSIZE )
+ activefds[comm->datafd] = 0;
+ /* delete the comm data fd specially from a non-select event loop */
+ if( xpa && xpa->seldel && comm->seldptr ){
+ (xpa->seldel)(comm->seldptr);
+ comm->seldptr = NULL;
+ }
+ /* close file */
+ xclose(comm->datafd);
+ }
+ /* reset data channel to impossible value */
+ comm->datafd = -1;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPAHandler
+ *
+ * Purpose: handle one request for an xpaset or xpaget
+ *
+ * Return: 0 on success, xpa error code on failure
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPAHandler (XPA xpa, int fd)
+#else
+int XPAHandler(xpa, fd)
+ XPA xpa;
+ int fd;
+#endif
+{
+ char tbuf[SZ_LINE];
+ char tbuf2[SZ_LINE];
+ char lbuf[SZ_LINE];
+ char cmd[SZ_LINE];
+ char target[SZ_LINE];
+ char method[SZ_LINE];
+ char ctmpl[SZ_LINE];
+ char ntmpl[SZ_LINE];
+ char *paramlist=NULL;
+ char *acl;
+ int save_ack;
+ int tcmd;
+ int got=0;
+ int lp=0;
+ int cmdfd=-1;
+ unsigned short port=0;
+ unsigned int ip=0;
+ struct timeval tv;
+ XPAComm comm=NULL, tcomm=NULL, xcomm=NULL, ocomm=NULL;
+ XPA txpa=NULL;
+ NS ns=NULL;
+ fd_set readfds;
+ FPRINTF((stderr, "%sXPAHandler: entering with fd %d\n", _sp, fd));
+
+ /* this is a defensive measure: we have to guard against an external
+ loop calling the XPA handler with a bogus XPA record. This has been
+ seen with the Tcl event loop.
+ */
+ for(txpa=xpahead; txpa!=NULL; txpa=txpa->next){
+ if( txpa == xpa ) break;
+ }
+ if( txpa == NULL ){
+ FPRINTF((stderr, "%sXPAHandler: xpa record %p is not in list\n",
+ _sp, xpa));
+ return(XPA_RTN_NOCMD);
+ }
+
+ /* this is a defensive measure: we have to ensure that there really
+ * is a request read. It is possible that a user-defined select loop
+ * might call us to handle a request that we had already handled
+ * (i.e. we handle a request but can't reset someone else's select flags)
+ */
+ FD_ZERO(&readfds);
+ FD_SET(fd, &readfds);
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ while( xselect(fd+1, &readfds, NULL, NULL, &tv) <=0 ){
+ if( xerrno == EINTR )
+ continue;
+ FPRINTF((stderr, "%sXPAHandler: xpa fd %d is not ready\n", _sp, fd));
+ return(XPA_RTN_NOCMD);
+ }
+
+ /* if this is a first connection, we create a new comm channel and exit */
+ if( fd == xpa->fd ){
+ if( (comm = CommNew(xpa, -1, 0, 0, NULL, NULL)) == NULL )
+ return(XPA_RTN_NOCONN);
+ /* Return to prevent xpa from finishing before other xpa's are started.
+ Otherwise, each xpa's associated with a target template will be
+ processed serially, which will defeat all the hard work done on
+ the client side to send data to servers when they ask for it.
+ */
+ else{
+ FPRINTF((stderr, "%sXPAHandler: returning after CommNew on fd %d\n",
+ _sp, fd));
+ got = XPA_RTN_OK;
+ goto end;
+ }
+ }
+
+ /* look for a currently active comm channel: cmd or data */
+ for(comm=xpa->commhead; comm!= NULL; comm=comm->next){
+ if( (fd == comm->cmdfd) || (fd == comm->datafd) ){
+ break;
+ }
+ }
+ /* make sure we have something */
+ if( comm == NULL ){
+ /* read extraenous message */
+ if( XPAGets(xpa, fd, tbuf, SZ_LINE, 1) >0 ){
+ got = XPA_RTN_UNCMD;
+ FPRINTF((stderr, "%sXPAHandler: fd %d received extra message:\n%s\n",
+ _sp, fd, tbuf));
+ }
+ else{
+ FPRINTF((stderr, "%sXPAHandler: no active comm record for fd %d\n",
+ _sp, fd));
+ got = XPA_RTN_NOCMD2;
+ }
+ goto error;
+ }
+ /* but don't recurse */
+ if( comm->status & XPA_STATUS_ACTIVE ){
+ FPRINTF((stderr, "%sXPAHandler: fd %d returning to avoid recursion\n",
+ _sp, fd));
+ got = 0;
+ goto end;
+ }
+ /* set current comm for this xpa */
+ ocomm = xpa->comm;
+ xpa->comm = comm;
+ /* no message sent yet */
+ comm->message = 0;
+
+ /* data ready on data channel: go right to data handling section */
+ if( fd == comm->datafd ){
+ FPRINTF((stderr, "%sXPAHandler: jumping to cb for %d\n", _sp, fd));
+ goto cb;
+ }
+
+ /* cmd channel: we are processing a new command */
+retry:
+ /* reset line buffer pointer for parsing */
+ lp = 0;
+ /* read next command */
+ if( XPAGets(xpa, comm->cmdfd, lbuf, SZ_LINE, stimeout) <=0 ){
+ FPRINTF((stderr, "%sXPAHandler: fd %d read EOF\n", _sp, comm->cmdfd));
+ got = XPA_RTN_OK;
+ goto eof;
+ }
+ /* new-lines imply we entered telnet mode on local host */
+ else if( (*lbuf == '\n') || (*lbuf == '\r') || !strcmp(lbuf, "telnet") ){
+ if( (mtype == XPA_UNIX) || LOCALIP(comm->cmdip) ){
+ if( comm->telnet == 0 )
+ XPAPuts(xpa, comm->cmdfd, "Entering telnet mode ...\n", stimeout);
+ comm->telnet = 1;
+ comm->ack = 0;
+ comm->datafd = comm->cmdfd;
+ stimeout = -1;
+ goto retry;
+ }
+ else{
+ XPAError(xpa, xpaMessbuf[XPA_RTN_NOCMD]);
+ got = XPA_RTN_NOCMD;
+ goto error;
+ }
+ }
+
+ FPRINTF((stderr, "%sXPAHandler: fd %d read command: %s",
+ _sp, comm->cmdfd, lbuf));
+
+ /* validate the command */
+ if( !word(lbuf, cmd, &lp) ){
+ FPRINTF((stderr, "%sXPAHandler: missing target for fd %d\n",
+ _sp, comm->cmd));
+ XPAError(xpa, xpaMessbuf[XPA_RTN_NOCMD]);
+ got = XPA_RTN_NOCMD;
+ goto error;
+ }
+
+ /* send help message (local support only) */
+ if( !strcmp(cmd, "help") ){
+ if( (mtype == XPA_UNIX) || LOCALIP(comm->cmdip) ){
+ XPAPuts(xpa, comm->cmdfd,
+ "xpaset|xpaget|xpainfo|xpaaccess [switches] class:name [params]\n",
+ stimeout);
+ XPAPuts(xpa, comm->cmdfd,
+ "switches:\n",
+ stimeout);
+ XPAPuts(xpa, comm->cmdfd,
+ "\t-a\t\tclient wants to accept() data connection\n",
+ stimeout);
+ XPAPuts(xpa, comm->cmdfd,
+ "\t-e <b|l>\tclient endian-ness: big(b) or little(l)\n",
+ stimeout);
+ XPAPuts(xpa, comm->cmdfd,
+ "\t-i id\t\tclient id string\n",
+ stimeout);
+ XPAPuts(xpa, comm->cmdfd,
+ "\t-p <proxyinfo>\t\tfrom xpans (e.g., for proxy processing)\n",
+ stimeout);
+ XPAPuts(xpa, comm->cmdfd,
+ "\t-n\t\tdon't ack back to client\n",
+ stimeout);
+ XPAPuts(xpa, comm->cmdfd,
+ "\t-t\t\tenter telnet mode (local only)\n",
+ stimeout);
+ /* we must be in telnet mode */
+ comm->telnet = 1;
+ comm->ack = 0;
+ goto retry;
+ }
+ else{
+ XPAError(xpa, xpaMessbuf[XPA_RTN_NOCMD]);
+ got = XPA_RTN_NOCMD;
+ goto error;
+ }
+ }
+
+ /* determine which command was specified */
+ if( !strcmp(cmd, "xpaset") )
+ tcmd = XPA_SET;
+ else if( !strcmp(cmd, "xpaget") )
+ tcmd = XPA_GET;
+ else if( !strcmp(cmd, "xpainfo") )
+ tcmd = XPA_INFO;
+ else if( !strcmp(cmd, "xpaaccess") )
+ tcmd = XPA_ACCESS;
+ else if( !strcmp(cmd, "xpadata") )
+ tcmd = XPA_DATA;
+ else if( !strcmp(cmd, "xpaaccept") )
+ tcmd = XPA_ACCEPT;
+ else if( !strcmp(cmd, "xpanagle") || !strcmp(cmd, "XPA$OK") )
+ tcmd = XPA_NAGLE;
+ else{
+ FPRINTF((stderr, "%sXPAHandler: unknown command '%s' for fd %d\n",
+ _sp, cmd, comm->cmdfd));
+ XPAError(xpa, xpaMessbuf[XPA_RTN_NOCMD]);
+ got = XPA_RTN_NOCMD;
+ goto error;
+ }
+
+ /* process switches */
+ while( word(lbuf, tbuf, &lp) ){
+ if( *tbuf != '-' )
+ break;
+ if( !strcmp(tbuf, "-a") ){
+ comm->mode |= COMM_CONNECT;
+ }
+ else if( !strcmp(tbuf, "-e") ){
+ if( word(lbuf, tbuf, &lp) ){
+ if( *tbuf == 'b' )
+ comm->cendian = "big";
+ else if( *tbuf == 'l' )
+ comm->cendian = "little";
+ }
+ else{
+ got = XPA_RTN_ILLCMD;
+ goto error;
+ }
+ }
+ else if( !strcmp(tbuf, "-f") ){
+ /* B.Schoenhammer@bit-field.de 2009-09-21 */
+#if HAVE_MINGW32==0
+ if( !word(lbuf, tbuf, &lp) || (sscanf(tbuf, "%p", &xcomm) != 1) ){
+#else
+ if( !word(lbuf, tbuf, &lp) || (sscanf(tbuf, "%x", &xcomm) != 1) ){
+#endif
+ got = XPA_RTN_ILLCMD;
+ goto done;
+ }
+ if( !word(lbuf, tbuf, &lp) || ((cmdfd = atoi(tbuf)) < 0) ){
+ got = XPA_RTN_ILLCMD;
+ goto done;
+ }
+ }
+ else if( !strcmp(tbuf, "-i") ){
+ if( word(lbuf, tbuf, &lp) ){
+ if( comm->id != NULL ) xfree(comm->id);
+ comm->id = xstrdup(tbuf);
+ }
+ else{
+ XPAError(xpa, xpaMessbuf[XPA_RTN_ILLCMD]);
+ got = XPA_RTN_ILLCMD;
+ goto error;
+ }
+ }
+ else if( !strcmp(tbuf, "-n") ){
+ comm->ack = 0;
+ }
+ else if( !strcmp(tbuf, "-p") ){
+ if( word(lbuf, tbuf, &lp) ){
+ if( comm->info != NULL ) xfree(comm->info);
+ comm->info = xstrdup(tbuf);
+ }
+ else{
+ XPAError(xpa, xpaMessbuf[XPA_RTN_ILLCMD]);
+ got = XPA_RTN_ILLCMD;
+ goto error;
+ }
+ }
+ else if( !strcmp(tbuf, "-t") ){
+ comm->telnet = 1;
+ }
+ else{
+ break;
+ }
+ }
+
+ /* make sure we have some sort of id */
+ if( comm->id == NULL ){
+ comm->id = xstrdup("?");
+ }
+
+ /* process nagle ack */
+ if( tcmd == XPA_NAGLE ){
+ /* if data is forthcoming, exit and wait for data */
+ if( comm->usebuf ){
+ got = XPA_RTN_OK;
+ goto end;
+ }
+ /* go process callback */
+ else{
+ goto cb;
+ }
+ }
+
+ /* connect (proxy) request: connect back to client */
+ if( tcmd == XPA_ACCEPT ){
+ FPRINTF((stderr, "%scmd is xpaaccept from client %d: %s", _sp, fd, lbuf));
+ /* syntax: xpaaccept <method> ... */
+ lp = 0;
+ if( !word(lbuf, tbuf, &lp) ||
+ strcmp(tbuf, "xpaaccept") ||
+ !word(lbuf, method, &lp) ){
+ got = -1;
+ goto error;
+ }
+ if( (cmdfd=XPAProxyConnect(xpa, method, &ip, &port, tbuf)) <0 ){
+ FPRINTF((stderr, "%sXPAProxyConnect failed for: %d\n", _sp, fd));
+ got = -1;
+ goto error;
+ }
+ if( (tcomm = CommNew(xpa, cmdfd, ip, port, method, NULL)) == NULL ){
+ got = XPA_RTN_NOCONN;
+ goto error;
+ }
+ else{
+ /* now exit and wait for client to send command */
+ FPRINTF((stderr,
+ "%sXPAHandler: fd %d exiting after proxy connect\n", _sp,
+ tcomm->cmdfd));
+ got = XPA_RTN_OK;
+ goto end;
+ }
+ }
+
+ /* data request: find associated command request, and process the command */
+ if( tcmd == XPA_DATA ){
+ FPRINTF((stderr, "%sXPAHandler: processing data fd %d\n",
+ _sp, comm->cmdfd));
+ /* find the cmd record with which this data is associated */
+ for(tcomm=xpa->commhead; tcomm!= NULL; tcomm=tcomm->next){
+ if( (tcomm == xcomm) && (tcomm->cmdfd == cmdfd) ){
+ break;
+ }
+ }
+ /* if we found an associated command, copy in data info and process */
+ if( tcomm ){
+ /* fill in command comm */
+ tcomm->datafd = comm->cmdfd;
+ tcomm->seldptr = comm->selcptr;
+ if( tcomm->dataname ) xfree(tcomm->dataname);
+ tcomm->dataname = xstrdup(comm->cmdname);
+ /* done with data comm */
+ CommFree(xpa, comm, 0);
+ /* reset comm pointers */
+ comm = tcomm;
+ xpa->comm = comm;
+ FPRINTF((stderr, "%sXPAHandler: found cmd fd %d for this data fd %d\n",
+ _sp, comm->cmdfd, comm->datafd));
+ if( comm->cmd == XPA_SET ){
+ FPRINTF((stderr, "%sXPAHandler: data %d returning await xpaset\n",
+ _sp, comm->datafd));
+ got = XPA_RTN_OK;
+ goto end;
+ }
+ else{
+ FPRINTF((stderr, "%sXPAHandler: data %d going on to process data\n",
+ _sp, comm->datafd));
+ goto cb;
+ }
+ }
+ else{
+ FPRINTF((stderr, "%sXPAHandler: data fd has no corresponding cmd %d\n",
+ _sp, comm->cmdfd));
+ XPAError(xpa, xpaMessbuf[XPA_RTN_ILLCMD]);
+ got = XPA_RTN_ILLCMD;
+ goto error;
+ }
+ }
+
+ /* for a command, the first non-switch word we found was the target */
+ if( *tbuf == '\0' ){
+ FPRINTF((stderr, "%sXPAHandler: missing target for fd %d\n",
+ _sp, comm->cmdfd));
+ XPAError(xpa, xpaMessbuf[XPA_RTN_NOCMD]);
+ got = XPA_RTN_NOCMD;
+ goto error;
+ }
+ else{
+ strcpy(target, tbuf);
+ }
+
+ /* validate the target (except for xpans for use as proxy) */
+ if( strcmp(xpa->xclass, XPANS_CLASS) || strcmp(xpa->name, XPANS_NAME) ){
+ XPAParseName(target, ctmpl, ntmpl, SZ_LINE);
+ if( (strcmp(ctmpl,"?") && !tmatch(xpa->xclass, ctmpl)) ||
+ (strcmp(ntmpl,"?") && !tmatch(xpa->name, ntmpl)) ){
+ got = XPA_RTN_NOTARG;
+ goto error;
+ }
+ }
+ if( comm->target != NULL ) xfree(comm->target);
+ comm->target = xstrdup(target);
+
+ /* the rest of the input string is paramlist */
+ paramlist = &(lbuf[lp]);
+ nowhite(paramlist, paramlist);
+ if( comm->paramlist != NULL ) xfree(comm->paramlist);
+ comm->paramlist = xstrdup(paramlist);
+ /* save command */
+ comm->cmd = tcmd;
+
+ FPRINTF((stderr,
+ "%sXPAHandler: processing command fd %d for target %s (%d)\n",
+ _sp, comm->cmdfd, target, comm->cmd));
+
+ /* a reserved command can only be called from the same host as the server,
+ or from local (unix) sockets */
+ lp = 0;
+ if( XPACmdLookupReserved(xpa, comm->paramlist, &lp) ){
+ comm->mode |= COMM_RESERVED;
+ }
+
+ /* set up command-specific info */
+ switch(comm->cmd){
+ case XPA_SET:
+ if( comm->mode & COMM_RESERVED ){
+ comm->usebuf = 1;
+ comm->useacl = guseacl && (mtype != XPA_UNIX);
+ acl = "s";
+ }
+ else if( xpa->receive_callback ){
+ comm->usebuf = (xpa->receive_mode & XPA_MODE_BUF);
+ comm->useacl = guseacl &&
+ (xpa->receive_mode & XPA_MODE_ACL) && (mtype != XPA_UNIX);
+ acl = "s";
+ }
+ else{
+ XPAError(xpa, xpaMessbuf[XPA_RTN_NOREC]);
+ got = XPA_RTN_NOREC;
+ goto error;
+ }
+ break;
+ case XPA_GET:
+ if( comm->mode & COMM_RESERVED ){
+ comm->usebuf = 1;
+ comm->useacl = guseacl && (mtype != XPA_UNIX);
+ acl = "g";
+ }
+ else if( xpa->send_callback ){
+ comm->usebuf = (xpa->send_mode & XPA_MODE_BUF);
+ comm->useacl = guseacl &&
+ (xpa->send_mode & XPA_MODE_ACL) && (mtype != XPA_UNIX);
+ acl = "g";
+ }
+ else{
+ XPAError(xpa, xpaMessbuf[XPA_RTN_NOSEND]);
+ got = XPA_RTN_NOSEND;
+ goto error;
+ }
+ break;
+ case XPA_INFO:
+ if( xpa->info_callback ){
+ comm->usebuf = 0;
+ comm->useacl = guseacl &&
+ (xpa->info_mode & XPA_MODE_ACL) && (mtype != XPA_UNIX);
+ acl = "i";
+ }
+ else{
+ XPAError(xpa, xpaMessbuf[XPA_RTN_NOINFO]);
+ got = XPA_RTN_NOINFO;
+ goto error;
+ }
+ break;
+ case XPA_ACCESS:
+ comm->usebuf = 0;
+ comm->useacl = guseacl && (mtype != XPA_UNIX);
+ /* may as well check access mode as well */
+ snprintf(tbuf2, SZ_LINE, "%sa%s", _sp, comm->paramlist?comm->paramlist:"");
+ acl = tbuf2;
+ break;
+ default:
+ FPRINTF((stderr, "%sXPAHandler: invalid access control check for fd %d\n",
+ _sp, comm->cmdfd));
+ XPAError(xpa, xpaMessbuf[XPA_RTN_NOCMD]);
+ got = XPA_RTN_NOCMD;
+ goto error;
+ }
+
+ /* perform access authentication */
+ if( comm->useacl ){
+ /* determine acl for this ip, if necessary */
+ if( comm->acl[comm->cmd] < 0 ){
+ comm->acl[comm->cmd] = XPAAclCheck(xpa, comm->cmdip, acl);
+ }
+ /* check acl */
+ if( comm->acl[comm->cmd] <= 0 ){
+ FPRINTF((stderr, "%sXPAHandler: fd %d FAILED acl check\n",
+ _sp, comm->cmdfd));
+ XPAError(xpa, xpaMessbuf[XPA_RTN_NOAUTH]);
+ got = XPA_RTN_NOAUTH;
+ goto error;
+ }
+ FPRINTF((stderr, "%sXPAHandler: fd %d passed acl check\n",
+ _sp, comm->cmdfd));
+ }
+
+ /* for telnet mode, just use cmdfd for data */
+ if( comm->telnet ){
+ comm->datafd = comm->cmdfd;
+ }
+ /* for xpainfo and no ack, just go to the vallback */
+ else if( (comm->cmd == XPA_INFO) && !comm->ack ){
+ goto cb;
+ }
+ /* in general, we must tell the client how we will handle data */
+ else{
+ if( comm->usebuf ){
+ if( comm->mode & COMM_CONNECT ){
+ snprintf(lbuf, SZ_LINE, "%s XPA$DATA accept %p %d (%s:%s %s)\n",
+ comm->id, comm, comm->cmdfd,
+ xpa->xclass, xpa->name, xpa->method);
+ }
+ else{
+ snprintf(lbuf, SZ_LINE, "%s XPA$DATA connect %p %d (%s:%s %s)\n",
+ comm->id, comm, comm->cmdfd,
+ xpa->xclass, xpa->name, xpa->method);
+ }
+ }
+ /* no data channel being set up */
+ else{
+ snprintf(lbuf, SZ_LINE, "%s XPA$NODATA (%s:%s %s)\n",
+ comm->id, xpa->xclass, xpa->name, xpa->method);
+ }
+ FPRINTF((stderr, "%sXPAHandler: fd %d sends string: %s",
+ _sp, comm->cmdfd, lbuf));
+ if( XPAPuts(xpa, comm->cmdfd, lbuf, stimeout) <= 0 ){
+ FPRINTF((stderr, "%sXPAHandler: fd %d couldn't send string: %s",
+ _sp, comm->cmdfd, lbuf));
+ got = -1;
+ goto error;
+ }
+ /* now exit and wait for nagle ack and for client to send data */
+ FPRINTF((stderr,
+ "%sXPAHandler: fd %d exiting to wait for nagle or connect req\n",
+ _sp, comm->cmdfd));
+ got = XPA_RTN_OK;
+ goto end;
+ }
+
+ /* data channel complete (or no data): ready to execute the user callback */
+cb:
+ /* we are now active */
+ comm->status |= XPA_STATUS_ACTIVE;
+
+ /* remove the current comm from the list of active fds,
+ in case the server callback re-enters the event loop */
+ XPAActive(xpa, comm, 0);
+
+ /* zero out buf and len, just to make sure (don't free buf, in case
+ application is using previous) */
+ comm->buf = NULL;
+ comm->len = 0;
+
+ /* if we are not ack'ing after callback, do it now so client can exit */
+ /* but don't do this for xpainfo/noack */
+ if( !comm->ack && (comm->cmd != XPA_INFO) ){
+ FPRINTF((stderr, "%sXPAHandler: sending OK to non-acking client %d %d\n",
+ _sp, comm->cmdfd, comm->datafd));
+ comm->ack = 1;
+ XPAOK(xpa);
+ comm->ack = 0;
+ }
+
+ /* process command */
+ switch(comm->cmd){
+ case XPA_GET:
+ FPRINTF((stderr,
+ "%sXPAHandler: processing xpaget for %d %d\n", _sp,
+ comm->cmdfd, comm->datafd));
+ /* check for a reserved command */
+ if( comm->mode & COMM_RESERVED ){
+ got = XPASendCommands(xpa->send_data, xpa, comm->paramlist,
+ &(comm->buf), &(comm->len));
+ }
+ else{
+ got = (xpa->send_callback)(xpa->send_data, xpa, comm->paramlist,
+ &(comm->buf), &(comm->len));
+ }
+ if( (got == 0) && (comm->buf != NULL) && (comm->len > 0) ){
+ if( XPAPutBuf(xpa, comm->datafd, comm->buf, comm->len, ltimeout) < 0 ){
+ PERROR(("XPAHandler write buf"));
+ XPAError(xpa, "XPA could not write data to client");
+ goto done;
+ }
+ }
+ if( (xpa->send_mode & XPA_MODE_FREEBUF) && (comm->buf != NULL) ){
+ if( xpa->comm->myfree != NULL ){
+ if( xpa->comm->myfree_ptr != NULL ){
+ xpa->comm->myfree(xpa->comm->myfree_ptr);
+ }
+ else{
+ xpa->comm->myfree(comm->buf);
+ }
+ }
+ else{
+ xfree(comm->buf);
+ }
+ }
+ /* send client a message, unless its already been done */
+ if( !comm->message ){
+ if( got )
+ XPAError(xpa, "error detected in send callback routine");
+ else
+ XPAOK(xpa);
+ }
+ FPRINTF((stderr,
+ "%sXPAHandler: finished xpaget for %d %d\n", _sp,
+ comm->cmdfd, comm->datafd));
+ break;
+ case XPA_SET:
+ FPRINTF((stderr,
+ "%sXPAHandler: processing xpaset for %d %d\n", _sp,
+ comm->cmdfd, comm->datafd));
+ /* check for a reserved command */
+ if( comm->mode & COMM_RESERVED ){
+ got=XPAReceiveCommands(xpa->receive_data, xpa,
+ comm->paramlist, NULL, 0);
+ }
+ else{
+ /* fill buf if necessary */
+ if( (comm->datafd >= 0) &&
+ comm->usebuf && (xpa->receive_mode & XPA_MODE_FILLBUF) ){
+ if(XPAGetBuf(xpa, comm->datafd, &(comm->buf), &(comm->len), -1) <0){
+ XPAError(xpa, xpaMessbuf[XPA_RTN_NODATA]);
+ FPRINTF((stderr, "%sXPAHandler: no data for XPAGetBuf on %d\n", _sp,
+ comm->datafd));
+ got = -1;
+ goto done;
+ }
+ else{
+ /* close the data fd now that we are done with it */
+ XPACloseData(xpa, comm);
+ }
+ }
+ /* execute the receive callback */
+ got = (xpa->receive_callback)(xpa->receive_data, xpa,
+ comm->paramlist, comm->buf, comm->len);
+ }
+ if( (xpa->receive_mode & XPA_MODE_FREEBUF) && (comm->buf != NULL) ){
+ if( xpa->comm->myfree != NULL ){
+ if( xpa->comm->myfree_ptr != NULL ){
+ xpa->comm->myfree(xpa->comm->myfree_ptr);
+ }
+ else{
+ xpa->comm->myfree(comm->buf);
+ }
+ }
+ else{
+ xfree(comm->buf);
+ }
+ }
+ /* send client an error message, unless its already been done */
+ if( !comm->message ){
+ if( got ){
+ XPAError(xpa, "error detected in receive callback routine");
+ }
+ else{
+ XPAOK(xpa);
+ }
+ }
+ FPRINTF((stderr,
+ "%sXPAHandler: finished xpaset for %d %d\n", _sp,
+ comm->cmdfd, comm->datafd));
+ break;
+ case XPA_INFO:
+ /* send OK before callback because we do not want the client waiting */
+ if( comm->ack )
+ XPAOK(xpa);
+ save_ack = comm->ack;
+ /* don't ever ack in callback */
+ comm->ack = 0;
+ /* execute the info callback -- don't bother checking for errors */
+ (xpa->info_callback)(xpa->info_data, xpa, comm->paramlist);
+ comm->ack = save_ack;
+ break;
+ case XPA_ACCESS:
+ /* return errors -- that how we know if we have access */
+ comm->ack = 1;
+ /* type is in paramlist */
+ if( comm->paramlist && *comm->paramlist ){
+ char *s;
+ for(s=comm->paramlist; *s; s++){
+ switch(*s){
+ case 'g':
+ if( !xpa->send_callback ){
+ XPAError(xpa, "no send callback (i.e., can't xpaget)");
+ goto done;
+ }
+ break;
+ case 'i':
+ if( !xpa->info_callback ){
+ XPAError(xpa, "no info callback (i.e., can't xpainfo)");
+ goto done;
+ }
+ break;
+ case 's':
+ if( !xpa->receive_callback ){
+ XPAError(xpa, "no receive callback (i.e., can't xpaset)");
+ goto done;
+ }
+ break;
+ case 'a':
+ break;
+ default:
+ XPAError(xpa, "unknown xpa access type");
+ goto done;
+ }
+ }
+ /* if we got here, its OK */
+ XPAOK(xpa);
+ }
+ /* no type, anything is OK */
+ else
+ XPAOK(xpa);
+ break;
+ default:
+ /* something is really wrong if we have no command */
+ FPRINTF((stderr, "%sXPAHandler: invalid command #%d for fd %d\n",
+ _sp, comm->cmd, comm->cmdfd));
+ got = XPA_RTN_NOCMD;
+ goto error;
+ break;
+ }
+
+done:
+ FPRINTF((stderr,
+ "%sXPAHandler: finished processing %d with status %d\n", _sp,
+ fd, got));
+ /* add xpa back to list of active ones */
+ XPAActive(xpa, comm, 1);
+
+ /* finalize comm record */
+ if( comm ){
+ /* close data channel */
+ XPACloseData(xpa, comm);
+ /* xpa is no longer active */
+ comm->status &= ~XPA_STATUS_ACTIVE;
+ }
+
+ /* join common code */
+ goto end;
+
+eof:
+ /* on eof, free up comm */
+ if( comm ){
+ ns = comm->ns;
+ CommFree(xpa, comm, 1);
+ comm = NULL;
+ if( ns && !ns->nproxy && !ns->nxpa ){
+ FPRINTF((stderr, "%sXPAHandler: closing ns %s\n", _sp, ns->name));
+ XPANSClose(xpa, ns);
+ }
+ }
+ /* join common code */
+ goto end;
+
+
+error:
+ FPRINTF((stderr, "%sXPAHandler ERROR: return code %d on %d\n",
+ _sp, got, fd));
+ /* add xpa back to list of active ones (but not comm) */
+ XPAActive(xpa, comm, 1);
+ /* xpa is no longer using this comm */
+ xpa->comm = NULL;
+ /* don't accidentally close ns on error */
+ if( comm ){
+ if( comm->ns )
+ CommFree(xpa, comm, 0);
+ else
+ CommFree(xpa, comm, 1);
+ comm = NULL;
+ }
+ /* join common code */
+ goto end;
+
+end:
+ /* if a free was requested by the callback, do it now when its safe */
+ if( xpa->status & XPA_STATUS_FREE ){
+ XPAFree(xpa);
+ }
+ /* restore old value of comm */
+ else{
+ xpa->comm = ocomm;
+ }
+
+ /* return the status */
+ return(got);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPAMode
+ *
+ * Purpose: parse the mode string and set flags
+ *
+ * Returns: none
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+void
+XPAMode (char *mode, int *flag, char *name, int mask, int def)
+#else
+void XPAMode(mode, flag, name, mask, def)
+ char *mode;
+ int *flag;
+ char *name;
+ int mask;
+ int def;
+#endif
+{
+ char tbuf[SZ_LINE];
+ char s[SZ_LINE];
+
+ /* keyword routine requires an input buffer that can be modified */
+ if( mode && *mode ){
+ strncpy(s, mode, SZ_LINE-1);
+ s[SZ_LINE-1] = '\0';
+ }
+ else
+ goto error;
+ /* look for the keyword=<value> string */
+ if( keyword(s, name, tbuf, SZ_LINE) ){
+ if( istrue(tbuf) )
+ *flag |= mask;
+ else
+ *flag &= ~mask;
+ return;
+ }
+ else
+ goto error;
+
+error:
+ if( def )
+ *flag |= mask;
+ else
+ *flag &= ~mask;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPAShortTimeout
+ *
+ * Purpose: return short (select, gets) timeout value
+ *
+ * Return: timeout in seconds
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPAShortTimeout (void)
+#else
+int XPAShortTimeout()
+#endif
+{
+ return(stimeout);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPALongTimeout
+ *
+ * Purpose: return long (fillbuf) timeout value
+ *
+ * Return: timeout in seconds
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPALongTimeout (void)
+#else
+int XPALongTimeout()
+#endif
+{
+ return(ltimeout);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPAReceiveLTimeout
+ *
+ * Purpose: modify long timeout value for this process
+ *
+ * Returns: 0 for success, -1 for failure
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPAReceiveLTimeout (void *client_data, void *call_data, char *paramlist,
+ char *buf, size_t len)
+#else
+int XPAReceiveLTimeout(client_data, call_data, paramlist, buf, len)
+ void *client_data;
+ void *call_data;
+ char *paramlist;
+ char *buf;
+ size_t len;
+#endif
+{
+ XPA xpa = (XPA)call_data;
+ char tbuf[SZ_LINE];
+ char *s;
+
+ if( paramlist && *paramlist ){
+ strncpy(tbuf, paramlist, SZ_LINE-1);
+ tbuf[SZ_LINE-1] = '\0';
+ nocr(tbuf);
+ culc(tbuf);
+ if( !strcmp(tbuf, "reset") ){
+ ltimeout = XPA_LONG_TIMEOUT;
+ if( (s=(char *)getenv("XPA_LONG_TIMEOUT")) != NULL )
+ ltimeout = atoi(s);
+ }
+ else{
+ ltimeout = atoi(tbuf);
+ }
+ return(0);
+ }
+ else{
+ XPAError(xpa, "missing long timeout value");
+ return(-1);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPASendLTimeout
+ *
+ * Purpose: return the long timeout for this process
+ *
+ * Returns: 0 for success, -1 for failure
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPASendLTimeout (void *client_data, void *call_data, char *paramlist,
+ char **buf, size_t *len)
+#else
+int XPASendLTimeout(client_data, call_data, paramlist, buf, len)
+ void *client_data;
+ void *call_data;
+ char *paramlist;
+ char **buf;
+ size_t *len;
+#endif
+{
+ XPA xpa = (XPA)call_data;
+ char tbuf[SZ_LINE];
+
+ snprintf(tbuf, SZ_LINE, "%d\n", ltimeout);
+ send(xpa_datafd(xpa), tbuf, strlen(tbuf), 0);
+ return(0);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPAReceiveSTimeout
+ *
+ * Purpose: modify short timeout value for this process
+ *
+ * Returns: 0 for success, -1 for failure
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPAReceiveSTimeout (void *client_data, void *call_data, char *paramlist,
+ char *buf, size_t len)
+#else
+int XPAReceiveSTimeout(client_data, call_data, paramlist, buf, len)
+ void *client_data;
+ void *call_data;
+ char *paramlist;
+ char *buf;
+ size_t len;
+#endif
+{
+ XPA xpa = (XPA)call_data;
+ char tbuf[SZ_LINE];
+ char *s;
+
+ if( paramlist && *paramlist ){
+ strncpy(tbuf, paramlist, SZ_LINE-1);
+ tbuf[SZ_LINE-1] = '\0';
+ nocr(tbuf);
+ culc(tbuf);
+ if( !strcmp(tbuf, "reset") ){
+ stimeout = XPA_SHORT_TIMEOUT;
+ if( (s=(char *)getenv("XPA_SHORT_TIMEOUT")) != NULL )
+ stimeout = atoi(s);
+ }
+ else{
+ stimeout = atoi(tbuf);
+ }
+ return(0);
+ }
+ else{
+ XPAError(xpa, "missing short timeout value");
+ return(-1);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPASendSTimeout
+ *
+ * Purpose: return the short timeout for this process
+ *
+ * Returns: 0 for success, -1 for failure
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPASendSTimeout (void *client_data, void *call_data, char *paramlist,
+ char **buf, size_t *len)
+#else
+int XPASendSTimeout(client_data, call_data, paramlist, buf, len)
+ void *client_data;
+ void *call_data;
+ char *paramlist;
+ char **buf;
+ size_t *len;
+#endif
+{
+ XPA xpa = (XPA)call_data;
+ char tbuf[SZ_LINE];
+
+ snprintf(tbuf, SZ_LINE, "%d\n", stimeout);
+ send(xpa_datafd(xpa), tbuf, strlen(tbuf), 0);
+ return(0);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPAVerbosity
+ *
+ * Purpose: return verbosity value
+ *
+ * Return: verbosity value (0, 1, 2)
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPAVerbosity (void)
+#else
+int XPAVerbosity()
+#endif
+{
+ return(verbosity);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPASigusr1
+ *
+ * Purpose: return flag for using sigusr1
+ *
+ * Return: 0 or 1
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPASigusr1 (void)
+#else
+int XPASigusr1()
+#endif
+{
+ return(sigusr1);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPAInitEnv
+ *
+ * Purpose: initialize the xpa environment
+ *
+ * Return: none
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+void
+XPAInitEnv (void)
+#else
+void XPAInitEnv()
+#endif
+{
+ char *s;
+
+ if( !tmpdir ){
+ /* determine the communication method */
+ mtype=XPAMethod(NULL);
+ /* enable access controls and port management */
+ if( mtype != XPA_UNIX ){
+ XPAAclNew(NULL, 0);
+ XPAPortNew(NULL, 0);
+ }
+ /* set short (select,gets) timeout, if necessary */
+ if( (s=(char *)getenv("XPA_SHORT_TIMEOUT")) != NULL )
+ stimeout = atoi(s);
+ /* set long (fillbuf) timeout, if necessary */
+ if( (s=(char *)getenv("XPA_LONG_TIMEOUT")) != NULL )
+ ltimeout = atoi(s);
+ /* set xpans connect timeout, if necessary */
+ if( (s=(char *)getenv("XPA_CONNECT_TIMEOUT")) != NULL )
+ ctimeout = atoi(s);
+ /* set nsdelay timeout, if necessary */
+ if( (s=(char *)getenv("XPA_NSDELAY")) != NULL )
+ nsdelay = atoi(s);
+ /* set retries, if necessary */
+ if( (s=(char *)getenv("XPA_RETRIES")) != NULL )
+ retries = atoi(s);
+ /* set verbosity level, if necessary */
+ if( (s=(char *)getenv("XPA_VERBOSITY")) != NULL ){
+ if( istrue(s) )
+ verbosity = XPA_VERBOSITY;
+ else if( isfalse(s) )
+ verbosity = 0;
+ else
+ verbosity = atoi(s);
+ if( verbosity < 0 )
+ verbosity = 0;
+ }
+ /* check for acl enable/disable */
+ if( (s=(char *)getenv("XPA_ACL")) != NULL )
+ guseacl = istrue(s);
+ /* check for timestamp on errors */
+ if( (s=(char *)getenv("XPA_TIMESTAMP_ERRORS")) != NULL )
+ etimestamp = istrue(s);
+ /* check for xpans register flag */
+ if( (s=(char *)getenv("XPA_NSREGISTER")) != NULL )
+ nsregister = istrue(s);
+ /* check for use of siguser1 */
+ if( (s=(char *)getenv("XPA_SIGUSR1")) != NULL )
+ sigusr1 = istrue(s);
+ /* check for version checking */
+ if( (s=(char *)getenv("XPA_VERSIONCHECK")) != NULL ){
+ if( istrue(s) )
+ vercheck = 1;
+ else if( isfalse(s) )
+ vercheck = 0;
+ else
+ vercheck = atoi(s);
+ }
+ /* check for io loop calling xpa */
+ if( (s=(char *)getenv("XPA_IOCALLSXPA")) != NULL ){
+ if( istrue(s) ){
+ XPAIOCallsXPA(1);
+ }
+ else if( isfalse(s) ){
+ XPAIOCallsXPA(0);
+ }
+ }
+ /* make sure we have a temp dir */
+ if( tmpdir == NULL ){
+ if( (s=(char *)getenv("XPA_TMPDIR")) != NULL )
+ tmpdir = xstrdup(s);
+ else if( (s=(char *)getenv("TMPDIR")) != NULL )
+ tmpdir = xstrdup(s);
+ else if( (s=(char *)getenv("TMP")) != NULL )
+ tmpdir = xstrdup(s);
+ else
+ tmpdir = xstrdup(XPA_TMPDIR);
+ }
+ /* create directory, if necessary */
+ xmkdir(tmpdir, 0777);
+ xchmod(tmpdir, 0777);
+#if HAVE_MINGW32==0
+ /* Disable SIGPIPE so we do not die if the client dies.
+ * Rather, we will get an EOF on reading or writing.
+ */
+ xsignal_sigpipe();
+#endif
+ /* platform-dependent startup */
+ xsocketstartup();
+ }
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPANSAdd
+ *
+ * Purpose: add this XPA to the name service
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPANSAdd (XPA xpa, char *host, char *mode)
+#else
+int XPANSAdd(xpa, host, mode)
+ XPA xpa;
+ char *host;
+ char *mode;
+#endif
+{
+ char username[SZ_LINE];
+ char tbuf[SZ_LINE];
+ char xmode[SZ_LINE];
+ char *cmd="add";
+ char *s;
+ NS ns;
+ struct passwd *pw;
+
+ /* sanity check */
+ if( !xpa )
+ return(0);
+
+ /* handle special case of the name server itself -- its a known entry */
+ if( !strcmp(xpa->name, XPANS_NAME) ){
+ return(0);
+ }
+
+ /* look for the proxy=<true|false> string */
+ if( mode ){
+ strncpy(xmode, mode, SZ_LINE-1);
+ xmode[SZ_LINE-1] = '\0';
+ if( keyword(xmode, "proxy", tbuf, SZ_LINE) && istrue(tbuf) ){
+ cmd="addproxy";
+ }
+ }
+
+ /* open a connection to the name service */
+ if( (ns=XPANSOpen(xpa, host, 1)) != NULL ){
+ /* get the user name, from the environment */
+ if( (s=(char *)getenv("XPA_LOGNAME")) != NULL )
+ strncpy(username, s, SZ_LINE-1);
+ else if( (s=(char *)getenv("LOGNAME")) != NULL )
+ strncpy(username, s, SZ_LINE-1);
+#if HAVE_MINGW32==0
+ /* this is a last resort */
+ else if( (pw=getpwuid(geteuid())) )
+ strncpy(username, pw->pw_name, SZ_LINE-1);
+#endif
+ /* if nothing good has happened, make it "unknown" */
+ if( *username == '\0' )
+ strcpy(username, "unknown");
+ /* null-terminate string */
+ username[SZ_LINE-1] = '\0';
+
+ /* write the command to add this xpa */
+ snprintf(tbuf, SZ_LINE, "%s %s %s:%s %s %s\n",
+ cmd, xpa->method, xpa->xclass, xpa->name, xpa->type, username);
+ if( XPAPuts(xpa, ns->fd, tbuf, stimeout) < 0 ){
+ return(-1);
+ }
+ /* get result */
+ if( XPAGets(xpa, ns->fd, tbuf, SZ_LINE, stimeout) >0 ){
+ if( !strncmp(tbuf, "XPA$OK", 6) ){
+ /* for proxy, we now must listen for xpa requests on this ns */
+ if( !strcmp(cmd, "addproxy") && xpa ){
+ if( CommNew(xpa, ns->fd, ns->ip, ns->port, ns->name, ns) ){
+ /* one more proxy is using this name server */
+ ns->nproxy += 1;
+ }
+ }
+ else{
+ /* one more access point is using this name server */
+ ns->nxpa += 1;
+ }
+ return(0);
+ }
+ else if( !strncmp(tbuf, "XPA$EXISTS", 10) )
+ return(0);
+ else
+ return(-1);
+ }
+ else{
+ return(-1);
+ }
+ }
+ else{
+ return(-1);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPANSDel
+ *
+ * Purpose: remove public knowledge of this XPA from the name service
+ *
+ * Returns: none
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPANSDel (XPA xpa, char *host, char *mode)
+#else
+int XPANSDel(xpa, host, mode)
+ XPA xpa;
+ char *host;
+ char *mode;
+#endif
+{
+ int got=0;
+ char tbuf[SZ_LINE];
+ char xmode[SZ_LINE];
+ char *cmd="del";
+ NS ns;
+
+ /* sanity check */
+ if( !xpa )
+ return(0);
+
+ /* handle special case of the name server itself -- its a known entry */
+ if( xpa->name && !strcmp(xpa->name, XPANS_NAME) ){
+ return(0);
+ }
+
+ /* if there is no method, just return */
+ if( (xpa->method == NULL) || (*xpa->method == '\0') ){
+ return(0);
+ }
+
+ /* look for the proxy=<true|false> string */
+ if( mode ){
+ strncpy(xmode, mode, SZ_LINE-1);
+ xmode[SZ_LINE-1] = '\0';
+ if( keyword(xmode, "proxy", tbuf, SZ_LINE) && istrue(tbuf) ){
+ cmd="delproxy";
+ }
+ }
+
+ /* open a connection to the name service */
+ if( (ns=XPANSOpen(xpa, host, -1)) != NULL ){
+ /* write the command to delete this xpa */
+ snprintf(tbuf, SZ_LINE, "%s %s\n", cmd, xpa->method);
+ XPAPuts(xpa, ns->fd, tbuf, stimeout);
+ /* get result */
+ if( XPAGets(xpa, ns->fd, tbuf, SZ_LINE, stimeout) >0 ){
+ if( !strncmp(tbuf, "XPA$OK", 6) ){
+ /* one less access point is using this name server */
+ ns->nxpa -= 1;
+ /* if there are no more access points using this xpans, close it */
+ if( !ns->nxpa && !ns->nproxy ){
+ XPANSClose(xpa, ns);
+ }
+ }
+ else{
+ got = -1;
+ }
+ }
+ else{
+ got = -1;
+ }
+ }
+ else
+ got = -1;
+ return(got);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPAVersionCheck
+ *
+ * Purpose: check our version vs. xpans version
+ *
+ * Returns: -1,0,1 for our<=>xpans
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPAVersionCheck (char *serv, char *nsv)
+#else
+int XPAVersionCheck(serv, nsv)
+ char *serv;
+ char *nsv;
+#endif
+{
+ int i;
+ int ip1=0;
+ int ip2=0;
+ int v1=0;
+ int v2=0;
+ int got=0;
+ int vsize=2;
+ char s1[SZ_LINE];
+ char s2[SZ_LINE];
+
+ /* return if not checking version */
+ if( vercheck <=0 )
+ return(0);
+ /* if either does not exist, its a mismatch */
+ if( !word(serv, s1, &ip1) || !word(nsv, s2, &ip2) )
+ return(1);
+ /* if strings are equal, versions are equal */
+ if( !strcasecmp(s1, s2) )
+ return(0);
+
+ /* format for version is maj.min.patch[be]beta */
+ newdtable(".be");
+ /* we check only the major and minor version for incompatibilities */
+ for(i=0; i<vsize; i++){
+ if( !word(serv, s1, &ip1) || !word(nsv, s2, &ip2) ){
+ break;
+ }
+ v1 = atoi(s1);
+ v2 = atoi(s2);
+ if( v1 > v2 ){
+ got = 1;
+ break;
+ }
+ if( v1 < v2 ){
+ got = -1;
+ break;
+ }
+ }
+
+ freedtable();
+ return(got);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPAVersionWarn
+ *
+ * Purpose: warn about mismatched versions
+ *
+ * Returns: NONE
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+void
+XPAVersionWarn (char *serv, char *nsv)
+#else
+void XPAVersionWarn(serv, nsv)
+ char *serv;
+ char *nsv;
+#endif
+{
+ /* return if not checking version */
+ if( vercheck <=0 )
+ return;
+
+ /* output warning */
+ fprintf(stderr,
+ "XPA$WARNING: version mismatch detected between XPA-enabled server (%s)\n", serv?serv:"unknown");
+ fprintf(stderr, "and xpans (%s).", nsv?nsv:"unknown");
+ fprintf(stderr, " You probably will get bad results.\n");
+ fprintf(stderr, "Please consider updating XPA to match the XPA-enabled server you are running.\n");
+
+ /* we did it */
+ vercheck--;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ *
+ * Public Routines
+ *
+ *
+ *----------------------------------------------------------------------------
+ */
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPAMethod
+ *
+ * Purpose: return communication method type
+ *
+ * Returns: XPA__INET, XPA_UNIX
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPAMethod (char *method)
+#else
+int XPAMethod(method)
+ char *method;
+#endif
+{
+ char *s1;
+
+ /* if no method is passed, we return the default method type */
+ if( method == NULL ){
+ if( mtype == 0 ){
+ s1 = (char *)getenv("XPA_METHOD");
+ if( s1 == NULL )
+ mtype = XPA_INET;
+ else if( !strcasecmp(s1, "inet") )
+ mtype = XPA_INET;
+ else if( !strcasecmp(s1, "localhost") ){
+ mtype = XPA_INET;
+ use_localhost = 1;
+ }
+ else if( !strcasecmp(s1, "unix") ){
+#if HAVE_SYS_UN_H
+ mtype = XPA_UNIX;
+#else
+ mtype = XPA_INET;
+ use_localhost = 1;
+#endif
+ }
+ else if( !strcasecmp(s1, "local") ){
+#if HAVE_SYS_UN_H
+ mtype = XPA_UNIX;
+#else
+ mtype = XPA_INET;
+ use_localhost = 1;
+
+#endif
+ }
+ else
+ mtype = XPA_INET;
+ }
+ return(mtype);
+ }
+ /* otherwise, we analyze the input method to get the type */
+ else{
+ /* inet is ip:port, else unix filename */
+ if( strchr(method, ':') != NULL )
+ return(XPA_INET);
+ else
+#if HAVE_SYS_UN_H
+ return(XPA_UNIX);
+#else
+ return(XPA_INET);
+#endif
+ }
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPANSMethod
+ *
+ * Purpose: return string containing name server method
+ *
+ * Returns: name server method string
+ *
+ *----------------------------------------------------------------------------
+ */
+
+#ifdef ANSI_FUNC
+char *
+XPANSMethod (char *host, int flag)
+#else
+char *XPANSMethod(host, flag)
+ char *host;
+ int flag;
+#endif
+{
+ char *s, *t;
+ char tbuf[SZ_LINE];
+ int i;
+ int ip;
+ int port;
+ unsigned int bip;
+ unsigned short bport;
+
+ switch( XPAMethod(host) ){
+ case XPA_INET:
+ if( (host != NULL) && (*host != '\0') )
+ strncpy(nsmethod, host, SZ_LINE-1);
+ else if( (s=(char *)getenv("XPA_NSINET")) != NULL )
+ strncpy(nsmethod, s, SZ_LINE-1);
+ else
+ strncpy(nsmethod, XPA_NSINET, SZ_LINE-1);
+ /* always null-terminate */
+ nsmethod[SZ_LINE-1] = '\0';
+ /* if flag, we want the XPA access ip and port, not the communication
+ channel between xpa servers and the name service */
+ if( flag ){
+ if( (s=strrchr(nsmethod, ':')) != NULL ){
+ /* this is where we will overwrite the port */
+ t = s+1;
+ /* get base port for default */
+ XPAParseIpPort(nsmethod, &bip, &bport);
+ newdtable(",");
+ for(ip=0, i=0; i<=flag; i++){
+ if( !word(t, tbuf, &ip) ){
+ *tbuf = '\0';
+ break;
+ }
+ }
+ freedtable();
+ if( *tbuf )
+ port = atoi(tbuf);
+ else
+ port = bport + flag;
+ snprintf(t, SZ_LINE, "%d", port);
+ }
+ }
+ break;
+#if HAVE_SYS_UN_H
+ case XPA_UNIX:
+ if( host != NULL )
+ strncpy(nsmethod, host, SZ_LINE-1);
+ else if( (s=(char *)getenv("XPA_NSUNIX")) != NULL )
+ strncpy(nsmethod, s, SZ_LINE-1);
+ else
+ snprintf(nsmethod, SZ_LINE, "%s/%s", tmpdir, XPA_NSUNIX);
+ /* always null-terminate */
+ nsmethod[SZ_LINE-1] = '\0';
+ /* if flag is set, we are getting the XPA access file, not the
+ socket file and we have to change the name slightly */
+ if( flag ){
+ /* replace the ending, if possible */
+ s = strrchr(nsmethod, '.');
+ t = strrchr(nsmethod, '/');
+ if( (s != NULL) && (s > t) )
+ *s = '\0';
+ snprintf(tbuf, SZ_LINE, ".xpa-%d", flag);
+ strcat(nsmethod, tbuf);
+ }
+ break;
+#endif
+ default:
+ if( (s=(char *)getenv("XPA_NSINET")) != NULL )
+ strncpy(nsmethod, s, SZ_LINE-1);
+ else
+ strncpy(nsmethod, XPA_NSINET, SZ_LINE-1);
+ /* always null-terminate */
+ nsmethod[SZ_LINE-1] = '\0';
+ break;
+ }
+
+ /* return the static method string */
+ return(nsmethod);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPAParseName
+ *
+ * Purpose: split the xpaname into a class and name string
+ *
+ * Results: none
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+void
+XPAParseName (char *xpaname, char *xclass, char *name, int len)
+#else
+void XPAParseName(xpaname, xclass, name, len)
+ char *xpaname;
+ char *xclass;
+ char *name;
+ int len;
+#endif
+{
+ char *s;
+ char *t;
+ char *cptr=NULL;
+ char *nptr=NULL;
+
+ /* if nothing is passed to us, allow everything */
+ if( (xpaname == NULL) || (*xpaname == '\0') ){
+ strncpy(xclass, "*", len-1);
+ strncpy(name, "*", len-1);
+ return;
+ }
+
+ /* split the xpaname into class and name */
+ s = xstrdup(xpaname);
+ if( (t=(char *)strchr(s, ':')) != NULL ){
+ cptr = s;
+ *t = '\0';
+ nptr = t+1;
+ }
+ else{
+ nptr = s;
+ cptr = "*";
+ }
+ if( *cptr == '\0' )
+ cptr = "*";
+ if( *nptr == '\0' )
+ nptr = "*";
+
+ strncpy(xclass, cptr, len-1);
+ strncpy(name, nptr, len-1);
+ xfree(s);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPAParseIpPort
+ *
+ * Purpose: split the host into ip and port
+ *
+ * Results: none
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPAParseIpPort (char *host, unsigned int *ip, unsigned short *port)
+#else
+int XPAParseIpPort(host, ip, port)
+ char *host;
+ unsigned int *ip;
+ unsigned short *port;
+#endif
+{
+ char *s1, *s2, *s3, *p1, *p2;
+ int got;
+
+ /* make sure we have something to work with */
+ if( (host == NULL) || (*host == '\0') )
+ return(0);
+
+ /* parse up the string and look for a port number (which is essential) */
+ s1 = xstrdup(host);
+ /* if we have a ",", null it out since what comes after is aux info */
+ if( (p1 = (char *)strchr(s1, ',')) != NULL ){
+ *p1 = '\0';
+ }
+ /* if we have a ":", we will null it out (so that what comes before is
+ the host name) and bump past it to point to the port */
+ if( (p1 = (char *)strchr(s1, ':')) == NULL ){
+ /* there is no ":", so the whole string is the port */
+ p1 = s1;
+ s2 = NULL;
+ } else {
+ /* null out ':' and bump port pointer */
+ *p1 = '\0';
+ p1++;
+ s2 = s1;
+ }
+
+ /* get port */
+ p2 = NULL;
+ if( p1 && !strcmp(p1, "$port") )
+ *port = XPA_NSPORT;
+ /* NB: port number might be followed by other stuff */
+ else
+ *port = (unsigned short)strtol(p1, &p2, 0);
+ /* check for bad port number -- we lose */
+ if( *port <=0 || (p1 == p2) || (p2 && (*p2 != '\0')) ){
+ *ip = 0;
+ *port = 0;
+ got = 0;
+ goto done;
+ }
+
+ /* get ip */
+ if( s2 && *s2 ){
+ /* see if this already is a hex address in network byte order */
+ *ip = strtoul16(s2, &s3);
+ if( *s3 == '\0' ){
+ got = 1;
+ goto done;
+ }
+ }
+ /* not hex or proxy -- convert ip string to an ip address */
+ if( (*ip = gethostip(s2)) == 0 ){
+ *port = 0;
+ got = 0;
+ }
+ else{
+ got = 1;
+ }
+
+done:
+ xfree(s1);
+ return(got);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPAParseUnixSocket
+ *
+ * Purpose: see if host is actually a unix socket file
+ *
+ * Results: 1 if its a socket, 0 otherwise
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPAParseUnixSocket (char *host)
+#else
+int XPAParseUnixSocket(host)
+ char *host;
+#endif
+{
+ struct stat buf;
+
+ /* see if its a file in the right directory */
+ if( !strncmp(host, tmpdir, strlen(tmpdir)) &&
+ !stat(host, &buf) ){
+ return(1);
+ }
+ else{
+ return(0);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPAOK
+ *
+ * Purpose: send an XPA OK message to the client
+ *
+ * Returns: 0 on success, -1 on error
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPAOK (XPA xpa)
+#else
+int XPAOK(xpa)
+ XPA xpa;
+#endif
+{
+ int len;
+ int status=0;
+ char tbuf[SZ_LINE];
+
+ /* make sure we have a valid struct */
+ if( (xpa == NULL) || (xpa_cmdfd(xpa) <0) )
+ return(-1);
+
+ /* send message, if necessary */
+ if( !(xpa_status(xpa) & XPA_STATUS_ACTIVE) || (xpa_ack(xpa) == 1) ){
+ snprintf(tbuf, SZ_LINE, "%s XPA$OK (%s:%s %s)\n",
+ xpa_id(xpa), xpa_class(xpa), xpa_name(xpa), xpa_method(xpa));
+ len = strlen(tbuf);
+ if( XPAPuts(xpa, xpa_cmdfd(xpa), tbuf, stimeout) != len ){
+ status = -1;
+ }
+ }
+
+ /* flag that there was a message sent for this xpa */
+ xpa->comm->message = 1;
+
+ /* return status */
+ return(status);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPATimestamp
+ *
+ * Purpose: generate string with current date/time
+ *
+ * Returns: time string (in static buffer)
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+char *
+XPATimestamp(void)
+#else
+char *XPATimestamp()
+#endif
+{
+ time_t t;
+ struct tm *lt;
+
+ *ctimebuf = '\0';
+ if( etimestamp ){
+ if( (t = time(NULL)) != (time_t)-1 ){
+ if( (lt = localtime(&t)) != NULL ){
+ snprintf(ctimebuf, SZ_LINE, " %02d/%02d/%d:%d:%d:%d",
+ lt->tm_mday, lt->tm_mon+1, lt->tm_year+1900,
+ lt->tm_hour, lt->tm_min, lt->tm_sec);
+ }
+ }
+ }
+ return ctimebuf;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPAError
+ *
+ * Purpose: send an XPA error message to the client
+ *
+ * Returns: none
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPAError (XPA xpa, char *s)
+#else
+int XPAError(xpa, s)
+ XPA xpa;
+ char *s;
+#endif
+{
+ int status=0;
+ int ip=0;
+ char tbuf[SZ_LINE];
+ char *t;
+ char *u;
+
+ /* make sure we have a valid struct */
+ if( (xpa == NULL) || (xpa_cmdfd(xpa) <0) )
+ return(-1);
+
+ /* send message, if necessary */
+ if( !(xpa_status(xpa) & XPA_STATUS_ACTIVE) || (xpa_ack(xpa) == 1) ){
+ t = xstrdup(s);
+ /* get rid of CR in message -- we add one at the end */
+ nowhite(t, t);
+ if( !strncmp(t, "XPA$", 4) )
+ word(t, tbuf, &ip);
+ u = (char *)xcalloc(strlen(t)+SZ_LINE, sizeof(char));
+ /* package up and write the message */
+ snprintf(u, SZ_LINE, "%s XPA$ERROR %s (%s:%s %s%s)\n",
+ xpa_id(xpa),
+ &t[ip], xpa_class(xpa), xpa_name(xpa), xpa_method(xpa),
+ XPATimestamp());
+ if( XPAPuts(xpa, xpa_cmdfd(xpa), u, stimeout) != (int)strlen(u) ){
+ status = -1;
+ }
+ if( t )
+ xfree(t);
+ if( u )
+ xfree(u);
+ }
+
+ /* flag that there was a message sent for this xpa */
+ xpa->comm->message = 1;
+
+ /* return status */
+ return(status);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPAMessage
+ *
+ * Purpose: send an XPA message to the client
+ *
+ * Returns: 0 on success, -1 on error
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPAMessage (XPA xpa, char *s)
+#else
+int XPAMessage(xpa, s)
+ XPA xpa;
+ char *s;
+#endif
+{
+ int status=0;
+ int ip=0;
+ char tbuf[SZ_LINE];
+ char *t;
+ char *u;
+
+ /* make sure we have a valid struct */
+ if( (xpa == NULL) || (xpa_cmdfd(xpa) <0) )
+ return(-1);
+
+ /* send message, if necessary */
+ if( !(xpa_status(xpa) & XPA_STATUS_ACTIVE) || (xpa_ack(xpa) == 1) ){
+ t = xstrdup(s);
+ /* get rid of CR in message -- we add one at the end */
+ nowhite(t, t);
+ if( !strncmp(t, "XPA$", 4) )
+ word(t, tbuf, &ip);
+ u = (char *)xcalloc(strlen(t)+SZ_LINE, sizeof(char));
+ /* package up and write the message */
+ snprintf(u, SZ_LINE, "%s XPA$MESSAGE %s (%s:%s %s%s)\n",
+ xpa_id(xpa),
+ &t[ip], xpa_class(xpa), xpa_name(xpa), xpa_method(xpa),
+ XPATimestamp());
+ if( XPAPuts(xpa, xpa_cmdfd(xpa), u, stimeout) != (int)strlen(u) ){
+ status = -1;
+ }
+ if( t )
+ xfree(t);
+ if( u )
+ xfree(u);
+ }
+
+ /* flag that there was a message sent for this xpa */
+ xpa->comm->message = 1;
+
+ /* return status */
+ return(status);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPAArgvParamlist
+ *
+ * Purpose: generate a paramlist string from an argv
+ *
+ * Results: allocated paramlist string
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+char *
+XPAArgvParamlist (int argc, char **argv, int start)
+#else
+char *XPAArgvParamlist(argc, argv, start)
+ int argc;
+ char **argv;
+ int start;
+#endif
+{
+ int plen;
+ int i;
+ char *paramlist;
+
+ /* get length of paramlist */
+ for(plen=0, i=start; i<argc; i++){
+ plen += (strlen(argv[i])+1);
+ }
+
+ /* allocate enough space for it */
+ if( (paramlist = (char *)xcalloc(plen+1, sizeof(char))) == NULL ){
+ return(NULL);
+ }
+
+ /* gather up the paramlist */
+ for(i=start; i<argc; i++){
+ strcat(paramlist, argv[i]);
+ strcat(paramlist, " ");
+ }
+
+ /* remove whitespace from beginning and end */
+ nowhite(paramlist, paramlist);
+
+ /* return paramlist */
+ return(paramlist);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPANSLookup
+ *
+ * Purpose: get name server matches
+ *
+ * Returns: number of matches
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPANSLookup (XPA xpa, char *tname, char *ttype,
+ char ***xclasses, char ***names, char ***methods, char ***infos)
+#else
+int XPANSLookup(xpa, tname, ttype, xclasses, names, methods, infos)
+ XPA xpa;
+ char *tname;
+ char *ttype;
+ char ***xclasses;
+ char ***names;
+ char ***methods;
+ char ***infos;
+#endif
+{
+ unsigned short port;
+ unsigned int ip;
+ int own;
+ int lp=0;
+ int got=0;
+ int nentry=100;
+ char *xtype;
+ char xclass[SZ_LINE];
+ char name[SZ_LINE];
+ char method[SZ_LINE];
+ char info[SZ_LINE];
+ char user[SZ_LINE];
+ char type[SZ_LINE];
+ char tbuf[SZ_LINE];
+ char lbuf[SZ_LINE];
+ NS ns;
+ XPA txpa;
+
+ /* do some initialization */
+ XPAInitEnv();
+
+ /* use "*" as default if no type was specified */
+ if( (ttype == NULL) || (*ttype == '\0') )
+ xtype = "*";
+ /* type 'a' means we are only trying to access */
+ else if( *ttype == 'a' )
+ xtype = "*";
+ else
+ xtype = ttype;
+
+ /* special case of name server */
+ if( !strcmp(tname, XPANS_NAME) ){
+ *xclasses = (char **)xmalloc(sizeof(char *));
+ *names = (char **)xmalloc(sizeof(char *));
+ *methods = (char **)xmalloc(sizeof(char *));
+ *infos = (char **)xmalloc(sizeof(char *));
+ (*xclasses)[0] = xstrdup(XPANS_CLASS);
+ (*names)[0] = xstrdup(XPANS_NAME);
+ (*methods)[0] = xstrdup(XPANSMethod(NULL, 1));
+ (*infos)[0] = xstrdup(XPA_DEF_CLIENT_INFO);
+ return(1);
+ }
+
+ /* special case of a string containing ip:port -- avoid trip to ns */
+ if( XPAParseIpPort(tname, &ip, &port) ){
+ *xclasses = (char **)xmalloc(sizeof(char *));
+ *names = (char **)xmalloc(sizeof(char *));
+ *methods = (char **)xmalloc(sizeof(char *));
+ *infos = (char **)xmalloc(sizeof(char *));
+ (*xclasses)[0] = xstrdup("?");
+ (*names)[0] = xstrdup("?");
+ (*methods)[0] = xstrdup(tname);
+ (*infos)[0] = xstrdup(XPA_DEF_CLIENT_INFO);
+ return(1);
+ }
+
+ /* special case of a string containing unix socket -- avoid trip to ns */
+ if( XPAParseUnixSocket(tname) ){
+ *xclasses = (char **)xmalloc(sizeof(char *));
+ *names = (char **)xmalloc(sizeof(char *));
+ *methods = (char **)xmalloc(sizeof(char *));
+ *infos = (char **)xmalloc(sizeof(char *));
+ (*xclasses)[0] = xstrdup("?");
+ (*names)[0] = xstrdup("?");
+ (*methods)[0] = xstrdup(tname);
+ (*infos)[0] = xstrdup(XPA_DEF_CLIENT_INFO);
+ return(1);
+ }
+
+ /* pre-allocate the various arrays to an absurd number */
+ *xclasses = (char **)xmalloc(nentry * sizeof(char *));
+ *names = (char **)xmalloc(nentry * sizeof(char *));
+ *methods = (char **)xmalloc(nentry * sizeof(char *));
+ *infos = (char **)xmalloc(nentry * sizeof(char *));
+
+ /* open a connection to the name service */
+ if( (ns=XPANSOpen(xpa, NULL, 0)) != NULL ){
+ while( word(tname, lbuf, &lp) ){
+ XPAParseName(lbuf, xclass, name, SZ_LINE);
+ /* write the command to add this xpa */
+ snprintf(tbuf, SZ_LINE, "lookup %s:%s %s %s\n",
+ xclass, name, xtype, nsusers);
+ FPRINTF((stderr, "%sXPANSLookup: sending command %s", _sp, tbuf));
+ XPAPuts(xpa, ns->fd, tbuf, stimeout);
+ /* read matches from the name server */
+ while( 1 ){
+ if( XPAGets(xpa, ns->fd, tbuf, SZ_LINE, stimeout) <=0 ){
+ FPRINTF((stderr, "%sXPANSLookup: unexpected EOF from xpans\n", _sp));
+ break;
+ }
+ FPRINTF((stderr, "%sXPANSLookup: receiving %s", _sp, tbuf));
+ /* XPA$<result> signals end of input */
+ if( !strncmp(tbuf, "XPA$", 4) )
+ break;
+ /* otherwise scan next line */
+ if( sscanf(tbuf, "%s %s %s %s %s %s\n",
+ xclass, name, type, method, user, info) != EOF ){
+ /* make sure this entry is not in the current process
+ (i.e., we can't ever xpa ourselves!) */
+ for(own=0, txpa=xpahead; txpa!=NULL; txpa=txpa->next){
+ if( !strcmp(txpa->xclass, xclass) && !strcmp(txpa->name, name) &&
+ !strcmp(txpa->method, method) ){
+ own = 1;
+ break;
+ }
+ }
+ /* if this xpa is in the current process, skip it */
+ if( own )
+ continue;
+ /* make sure we have enough space */
+ if( got >= nentry ){
+ nentry *= 2;
+ *xclasses = (char **)xrealloc(*xclasses, nentry * sizeof(char *));
+ *names = (char **)xrealloc(*names, nentry * sizeof(char *));
+ *methods = (char **)xrealloc(*methods, nentry * sizeof(char *));
+ *infos = (char **)xrealloc(*infos, nentry * sizeof(char *));
+ }
+ /* add this entry to the list */
+ (*xclasses)[got] = xstrdup(xclass);
+ (*names)[got] = xstrdup(name);
+ (*methods)[got] = xstrdup(method);
+ (*infos)[got] = xstrdup(info);
+ got++;
+ }
+ }
+ }
+ /* if we did not add to an xpa record, close up here */
+ if( xpa == NULL ){
+ XPANSClose(NULL, ns);
+ }
+ }
+ /* reallocate the exact number of buffers we have */
+ if( got > 0 ){
+ *xclasses = (char **)xrealloc(*xclasses, got * sizeof(char *));
+ *names = (char **)xrealloc(*names, got * sizeof(char *));
+ *methods = (char **)xrealloc(*methods, got * sizeof(char *));
+ *infos = (char **)xrealloc(*infos, got * sizeof(char *));
+ }
+ else{
+ xfree(*xclasses);
+ xfree(*names);
+ xfree(*methods);
+ xfree(*infos);
+ }
+ FPRINTF((stderr, "%sXPANSLookup: found %d entries\n", _sp, got));
+ return(got);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPANSClose
+ *
+ * Purpose: close connection to the name service
+ *
+ * Returns: none
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPANSClose (XPA xpa, NS ns)
+#else
+int XPANSClose(xpa, ns)
+ XPA xpa;
+ NS ns;
+#endif
+{
+ NS cur;
+ XPAComm comm, tcomm;
+
+ if( !ns )
+ return(-1);
+
+ /* remove from xpa list */
+ if( xpa ){
+ if( xpa->nshead ){
+ if( xpa->nshead == ns ){
+ xpa->nshead = ns->next;
+ }
+ else{
+ for(cur=xpa->nshead; cur!=NULL; cur=cur->next){
+ if( cur->next == ns ){
+ cur->next = ns->next;
+ break;
+ }
+ }
+ }
+ }
+ /* close comms associated with this ns */
+ for(comm=xpa->commhead; comm!=NULL; ){
+ tcomm = comm->next;
+ if( comm->ns == ns ){
+ CommFree(xpa, comm, 0);
+ }
+ comm = tcomm;
+ }
+ }
+
+ /* close socket */
+ if( ns->fd >=0 ){
+ xclose(ns->fd);
+ }
+ /* free up space */
+ if( ns->method) xfree(ns->method);
+ if( ns->host ) xfree(ns->host);
+ if( ns->name ) xfree(ns->name);
+ if( ns ) xfree(ns);
+ return(0);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPANSKeepAlive
+ *
+ * Purpose: send a 1-byte keep-alive packet to each name server
+ *
+ * Returns: -1 on error, else 1
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPANSKeepAlive (XPA xpa, int type)
+#else
+ int XPANSKeepAlive(xpa, type)
+ XPA xpa;
+ int type;
+#endif
+{
+ NS ns;
+ int got=0;
+
+ /* sanity check */
+ if( !xpa )
+ return(-1);
+
+ /* use default type, if none specified */
+ if( !type )
+ type=DEF_KA_TYPE;
+
+ /* send keep-alive to deserving xpans instances */
+ for(ns=xpa->nshead; ns!=NULL; ns=ns->next){
+ if( ((type&1) && (ns->nxpa>0)) || ((type&2) && (ns->nproxy>0)) ){
+ FPRINTF((stderr, "%sXPANSKeepAlive: sending ka to %d\n", _sp, ns->fd));
+#if USE_KA_OOB
+ /* send as out of band data to avoid mixing with normal data stream */
+ got = send(ns->fd, "\n", 1, MSG_OOB);
+#else
+ /* cisco routers can clear the URG flag by default, so use in-band */
+ got = send(ns->fd, "\n", 1, 0);
+#endif
+ }
+ }
+ return(got);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPANew
+ *
+ * Purpose: add a new xpa name to a process to allow external
+ * process to read/write data associated with this name.
+ *
+ * If the send callback is defined, it can send to an external process.
+ * If the receive callback is defined, it can receive from an external process.
+ *
+ * Returns: xpa handle associated with this class.name or NULL
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+XPA
+XPANew (char *xclass, char *name, char *help,
+ SendCb send_callback, void *send_data, char *send_mode,
+ ReceiveCb rec_callback, void *rec_data, char *rec_mode)
+#else
+XPA XPANew(xclass, name, help,
+ send_callback, send_data, send_mode,
+ rec_callback, rec_data, rec_mode)
+ char *xclass;
+ char *name;
+ char *help;
+ SendCb send_callback;
+ void *send_data;
+ char *send_mode;
+ ReceiveCb rec_callback;
+ void *rec_data;
+ char *rec_mode;
+#endif
+{
+ unsigned short port;
+ unsigned int ip;
+ int got;
+ int oum;
+ int keep_alive=1;
+ int reuse_addr=1;
+ char tbuf[SZ_LINE];
+ char tbuf2[SZ_LINE];
+ char tfile[SZ_LINE];
+ char *tfptr;
+ XPA xpa;
+ socklen_t slen = sizeof(struct sockaddr_in);
+ struct sockaddr_in sock_in;
+#if HAVE_SYS_UN_H
+ struct sockaddr_un sock_un;
+#endif
+
+ /* do some initialization */
+ XPAInitEnv();
+ /* init the list of reserved commands */
+ XPAInitReserved();
+
+ /* we need a name, but no ":" allowed in the name */
+ if( (name == NULL) || (*name == '\0') || strchr(name, ':') )
+ return(NULL);
+
+ /* limit the size of the xclass and name designation */
+ if( xclass && *xclass && (strlen(xclass) > XPA_NAMELEN) ){
+ if( verbosity )
+ fprintf(stderr, "XPA$ERROR: class designator too long\n");
+ return(NULL);
+ }
+ if( strlen(name) > XPA_NAMELEN ){
+ if( verbosity )
+ fprintf(stderr, "XPA$ERROR: name designator too long\n");
+ return(NULL);
+ }
+
+ /* we need either a send or a receive or both */
+ if( (send_callback == NULL) && (rec_callback == NULL ) ){
+ if( verbosity )
+ fprintf(stderr, "XPA$ERROR: requires send and/or receive callback\n");
+ return(NULL);
+ }
+
+ /* allocate xpa struct */
+ if( (xpa = (XPA)xcalloc(1, sizeof(XPARec))) == NULL )
+ return(NULL);
+
+ /* fill in the blanks */
+ xpa->version = xstrdup(XPA_VERSION);
+ xpa->type = (char *)xcalloc(10, sizeof(char));
+ if( xclass && *xclass )
+ xpa->xclass = xstrdup(xclass);
+ else
+ xpa->xclass = xstrdup("*");
+ xpa->name = xstrdup(name);
+ xpa->help = xstrdup(help);
+ /* set the value of the server big-endian-ness */
+ xpa->sendian = XPAEndian() ? xstrdup("big") : xstrdup("little");
+
+ /* fill in send information */
+ if( send_callback != NULL ){
+ xpa->send_callback = send_callback;
+ xpa->send_data = send_data;
+ strcat(xpa->type, "g");
+ /* process the mode string */
+ xpa->send_mode = XPA_DEF_MODE_SEND;
+ XPAMode(send_mode, &(xpa->send_mode), "freebuf", XPA_MODE_FREEBUF,1);
+ XPAMode(send_mode, &(xpa->send_mode), "acl", XPA_MODE_ACL, 1);
+ }
+
+ /* fill in receive information */
+ if( rec_callback != NULL ){
+ xpa->receive_callback = rec_callback;
+ xpa->receive_data = rec_data;
+ strcat(xpa->type, "s");
+ /* process the mode string */
+ xpa->receive_mode = XPA_DEF_MODE_REC;
+ XPAMode(rec_mode, &(xpa->receive_mode), "buf", XPA_MODE_BUF, 1);
+ XPAMode(rec_mode, &(xpa->receive_mode), "fillbuf", XPA_MODE_FILLBUF, 1);
+ XPAMode(rec_mode, &(xpa->receive_mode), "freebuf", XPA_MODE_FREEBUF, 1);
+ XPAMode(rec_mode, &(xpa->receive_mode), "acl", XPA_MODE_ACL, 1);
+ }
+
+ /* set up communication method */
+ switch(mtype){
+ case XPA_INET:
+ /* open a socket and fill in socket information */
+ if( (xpa->fd = xsocket(AF_INET, SOCK_STREAM, 0)) < 0 )
+ goto error;
+ setsockopt(xpa->fd, SOL_SOCKET, SO_KEEPALIVE,
+ (char *)&keep_alive, sizeof(keep_alive));
+ setsockopt(xpa->fd, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&reuse_addr, sizeof(reuse_addr));
+ memset((char *)&sock_in, 0, sizeof(sock_in));
+ sock_in.sin_family = AF_INET;
+ /* localhost only */
+ if( use_localhost )
+ sock_in.sin_addr.s_addr = htonl(gethostip("$localhost"));
+ /* any address will do */
+ else
+ sock_in.sin_addr.s_addr = htonl(INADDR_ANY);
+ /* handle special case of xpans port */
+ if( !strcmp(xpa->name, XPANS_NAME) ){
+ XPAParseIpPort(XPANSMethod(NULL, 1), &ip, &port);
+ sock_in.sin_port = htons(port);
+ }
+ else{
+ sock_in.sin_port = htons(XPAPort(xpa));
+ }
+ /* bind to the ip:port */
+ if( xbind(xpa->fd, (struct sockaddr *)&sock_in, sizeof(sock_in)) < 0 )
+ goto error;
+ /* we now can determine which port the system assigned */
+ if( getsockname(xpa->fd, (struct sockaddr *)&sock_in, &slen) < 0 )
+ goto error;
+ else{
+ /* ip:port is the method */
+ gethost(tbuf2, SZ_LINE);
+ snprintf(tbuf, SZ_LINE, "%x:%d",
+ gethostip(tbuf2), ntohs(sock_in.sin_port));
+ xpa->method = xstrdup(tbuf);
+ }
+ break;
+#if HAVE_SYS_UN_H
+ case XPA_UNIX:
+ /* open a socket and fill in socket information */
+ if( (xpa->fd = xsocket(AF_UNIX, SOCK_STREAM, 0)) < 0 )
+ goto error;
+ setsockopt(xpa->fd, SOL_SOCKET, SO_KEEPALIVE,
+ (char *)&keep_alive, sizeof(keep_alive));
+ setsockopt(xpa->fd, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&reuse_addr, sizeof(reuse_addr));
+ memset((char *)&sock_in, 0, sizeof(sock_in));
+ sock_un.sun_family = AF_UNIX;
+ /* handle special case of xpans */
+ if( !strcmp(xpa->name, XPANS_NAME) ){
+ strcpy(tbuf, XPANSMethod(NULL, 1));
+ }
+ else{
+ /* get filename part, composed of class and name and unique id */
+ snprintf(tfile, SZ_LINE, "%s_%s.%d",
+ xpa->xclass, xpa->name, (int)getpid());
+ /* change "/" to "_" for filename */
+ for(tfptr = tfile; *tfptr != '\0'; tfptr++){
+ if( *tfptr == '/' )
+ *tfptr = '_';
+ }
+ /* create full pathname */
+ snprintf(tbuf, SZ_LINE, "%s/%s", tmpdir, tfile);
+ }
+ /* delete old copy */
+ unlink (tbuf);
+ strcpy(sock_un.sun_path, tbuf);
+ /* unset umask so that everyone can read and write */
+ oum = umask(0);
+ /* bind to the file */
+ got = xbind(xpa->fd, (struct sockaddr *)&sock_un, sizeof(sock_un));
+ /* reset umask */
+ umask(oum);
+ /* now check for bind error */
+ if( got < 0 )
+ goto error;
+ /* path is the method */
+ xpa->method = xstrdup(tbuf);
+ break;
+#endif
+ default:
+ goto error;
+ }
+
+ /* listen for connections */
+ if( listen(xpa->fd, XPA_MAXLISTEN) < 0 )
+ goto error;
+
+ /* make sure we close on exec */
+ xfcntl(xpa->fd, F_SETFD, FD_CLOEXEC);
+
+ /* add this xpa to end of list of xpas */
+ XPAListAdd(&xpahead, xpa);
+
+ /* publish this entry to the world */
+ if( nsregister )
+ XPANSAdd(xpa, NULL, NULL);
+
+ /* make it active */
+ XPAActive(xpa, NULL, 1);
+
+#if NO_AUTOMATIC_HAVE_ATEXIT
+ /* register XPA atexit funtion */
+ if( !atexitinit ){
+ atexit(_XPAAtExit);
+ atexitinit = getpid();
+ }
+#endif
+
+ /* return good news */
+ return(xpa);
+
+error:
+ if( verbosity ){
+ perror("XPANew");
+ }
+ _XPAFree(xpa);
+ return(NULL);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Routine: XPAFree
+ *
+ * Purpose: free up alloc'ed memory in the XPA record structure
+ *
+ * Results: 0 on success, -1 for failure
+ *
+ *---------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPAFree (XPA xpa)
+#else
+int XPAFree(xpa)
+ XPA xpa;
+#endif
+{
+ /* if status is active, just flag eventual need to free and exit */
+ if( _XPAValid(xpahead, xpa, XPA_ACLS) ){
+ if( xpa_status(xpa) & XPA_STATUS_ACTIVE ){
+ xpa->status |= XPA_STATUS_FREE;
+ FPRINTF((stderr, "%sXPAFree: marking xpa struct for later free'ing\n",
+ _sp));
+ return(0);
+ }
+ else{
+ /* call the primitive routine */
+ FPRINTF((stderr, "%sXPAFree: freeing xpa struct\n", _sp));
+ return(_XPAFree(xpa));
+ }
+ }
+ else{
+ return(-1);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPAInfoNew
+ *
+ * Purpose: add a new xpa name to a process to allow external
+ * process to send info messages
+ *
+
+ * Returns: xpa handle associated with this class.name or NULL
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+XPA
+XPAInfoNew (char *xclass, char *name,
+ InfoCb info_callback, void *info_data, char *info_mode)
+#else
+XPA XPAInfoNew(xclass, name, info_callback, info_data, info_mode)
+ char *xclass;
+ char *name;
+ InfoCb info_callback;
+ void *info_data;
+ char *info_mode;
+#endif
+{
+ int got;
+ int oum;
+ int keep_alive=1;
+ int reuse_addr=1;
+ char tbuf[SZ_LINE];
+ char tbuf2[SZ_LINE];
+ char tfile[SZ_LINE];
+ char *tfptr;
+ XPA xpa;
+ socklen_t slen = sizeof(struct sockaddr_in);
+ struct sockaddr_in sock_in;
+#if HAVE_SYS_UN_H
+ struct sockaddr_un sock_un;
+#endif
+
+ /* do some initialization */
+ XPAInitEnv();
+ /* init the list of reserved commands */
+ XPAInitReserved();
+
+ /* we need a name, but no ":" allowed in the name */
+ if( (name == NULL) || (*name == '\0') || strchr(name, ':') )
+ return(NULL);
+
+ /* we need an info callback */
+ if( info_callback == NULL ){
+ if( verbosity ){
+ fprintf(stderr, "XPA$ERROR: requires info callback\n");
+ }
+ return(NULL);
+ }
+
+ /* allocate xpa struct */
+ if( (xpa = (XPA)xcalloc(1, sizeof(XPARec))) == NULL )
+ return(NULL);
+
+ xpa->version = xstrdup(XPA_VERSION);
+ xpa->type = (char *)xcalloc(10, sizeof(char));
+ /* fill in the blanks */
+ if( xclass != NULL )
+ xpa->xclass = xstrdup(xclass);
+ else
+ xpa->xclass = xstrdup("*");
+ xpa->name = xstrdup(name);
+
+ /* fill in send information */
+ xpa->info_callback = info_callback;
+ xpa->info_data = info_data;
+ strcat(xpa->type, "i");
+ /* process the mode string */
+ xpa->info_mode = XPA_DEF_MODE_INFO;
+ XPAMode(info_mode, &(xpa->info_mode), "acl", XPA_MODE_ACL, 1);
+
+ /* set up communication method */
+ switch(mtype){
+ case XPA_INET:
+ /* open a socket and fill in socket information */
+ if( (xpa->fd = xsocket(AF_INET, SOCK_STREAM, 0)) < 0 )
+ goto error;
+ setsockopt(xpa->fd, SOL_SOCKET, SO_KEEPALIVE,
+ (char *)&keep_alive, sizeof(keep_alive));
+ setsockopt(xpa->fd, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&reuse_addr, sizeof(reuse_addr));
+ memset((char *)&sock_in, 0, sizeof(sock_in));
+ sock_in.sin_family = AF_INET;
+ /* localhost only */
+ if( use_localhost )
+ sock_in.sin_addr.s_addr = htonl(gethostip("$localhost"));
+ /* any address will do */
+ else
+ sock_in.sin_addr.s_addr = htonl(INADDR_ANY);
+ sock_in.sin_port = htons(XPAPort(xpa));
+ /* bind to the ip:port */
+ if( xbind(xpa->fd, (struct sockaddr *)&sock_in, sizeof(sock_in)) < 0 )
+ goto error;
+ /* we now can determine which port the system assigned */
+ if( getsockname(xpa->fd, (struct sockaddr *)&sock_in, &slen) < 0 )
+ goto error;
+ else{
+ /* ip:port is the method */
+ gethost(tbuf2, SZ_LINE);
+ snprintf(tbuf, SZ_LINE, "%x:%d",
+ gethostip(tbuf2), ntohs(sock_in.sin_port));
+ xpa->method = xstrdup(tbuf);
+ }
+ break;
+#if HAVE_SYS_UN_H
+ case XPA_UNIX:
+ /* get filename part, composed of class and name and unique id */
+ snprintf(tfile, SZ_LINE, "%s_%s.%d",
+ xpa->xclass, xpa->name, (int)getpid());
+ /* change "/" to "_" for filename */
+ for(tfptr = tfile; *tfptr != '\0'; tfptr++){
+ if( *tfptr == '/' )
+ *tfptr = '_';
+ }
+ /* create full pathname */
+ snprintf(tbuf, SZ_LINE, "%s/%s", tmpdir, tfile);
+ /* delete old copy */
+ unlink (tbuf);
+ /* open a socket and fill in socket information */
+ if( (xpa->fd = xsocket(AF_UNIX, SOCK_STREAM, 0)) < 0 )
+ goto error;
+ setsockopt(xpa->fd, SOL_SOCKET, SO_KEEPALIVE,
+ (char *)&keep_alive, sizeof(keep_alive));
+ memset((char *)&sock_in, 0, sizeof(sock_in));
+ sock_un.sun_family = AF_UNIX;
+ strcpy(sock_un.sun_path, tbuf);
+ /* unset umask so that everyone can read and write */
+ oum = umask(0);
+ /* bind to the file */
+ got = xbind(xpa->fd, (struct sockaddr *)&sock_un, sizeof(sock_un));
+ /* reset umask */
+ umask(oum);
+ /* now check for bind error */
+ if( got < 0 )
+ goto error;
+ /* path is the method */
+ xpa->method = xstrdup(tbuf);
+ break;
+#endif
+ default:
+ goto error;
+ }
+
+ /* listen for connections */
+ if( listen(xpa->fd, XPA_MAXLISTEN) < 0 )
+ goto error;
+
+ /* make sure we close on exec */
+ xfcntl(xpa->fd, F_SETFD, FD_CLOEXEC);
+
+ /* add this xpa to end of list of xpas */
+ XPAListAdd(&xpahead, xpa);
+
+ /* publish this entry to the world */
+ if( nsregister )
+ XPANSAdd(xpa, NULL, NULL);
+
+ /* make it active */
+ XPAActive(xpa, NULL, 1);
+
+#if NO_AUTOMATIC_HAVE_ATEXIT
+ /* register XPA atexit funtion */
+ if( !atexitinit ){
+ atexit(_XPAAtExit);
+ atexitinit = getpid();
+ }
+#endif
+
+ /* return good news */
+ return(xpa);
+
+error:
+ XPAFree(xpa);
+ return(NULL);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPAPoll
+ *
+ * Purpose: non-blocking handling of XPA access points
+ * timeout in millisecs, but if negative, no timeout is used
+ *
+ * Returns: number of requests processed (if maxreq >=0)
+ * number of requests pending (if maxreq <0)
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPAPoll (int msec, int maxreq)
+#else
+int XPAPoll(msec, maxreq)
+ int msec;
+ int maxreq;
+#endif
+{
+ int sgot;
+ int got=0;
+ fd_set readfds;
+ struct timeval tv;
+ struct timeval *tvp;
+
+again:
+ if( msec >= 0 ){
+ tv.tv_sec = msec / 1000;
+ tv.tv_usec = (msec % 1000) * 1000;
+ tvp = &tv;
+ }
+ /* negative value means just block and wait */
+ else{
+ tvp = NULL;
+ }
+ FD_ZERO(&readfds);
+ if( XPAAddSelect(NULL, &readfds) ){
+ sgot = xselect(swidth, &readfds, NULL, NULL, tvp);
+ /* error -- what should we do? */
+ if( sgot < 0 ){
+ if( xerrno == EINTR )
+ goto again;
+ if( verbosity ){
+ perror("XPAPoll() select");
+ }
+ exit(1);
+ }
+ /* timeout -- just return */
+ else if( sgot == 0 )
+ ;
+ /* finally ... something to do */
+ else{
+ /* if maxreq < 0, just return how many are ready */
+ if( maxreq < 0 )
+ return(sgot);
+ else
+ got = XPAProcessSelect(&readfds, maxreq);
+ }
+ }
+ return(got);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPAMainLoop
+ *
+ * Purpose: non-X programs event loop for handling XPA
+ *
+ * Returns: none
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPAMainLoop (void)
+#else
+int XPAMainLoop()
+#endif
+{
+ int sgot;
+ int got=0;
+ fd_set readfds;
+
+
+ FD_ZERO(&readfds);
+ while( XPAAddSelect(NULL, &readfds) ){
+ FPRINTF((stderr, "%sXPAMainLoop: waiting for select() ...\n", _sp));
+ sgot = xselect(swidth, &readfds, NULL, NULL, NULL);
+ FPRINTF((stderr, "%sXPAMainLoop: select() returned: %d\n", _sp, sgot));
+ /* error -- what should we do? */
+ if( sgot < 0 ){
+ if( xerrno == EINTR ){
+ FD_ZERO(&readfds);
+ continue;
+ }
+ if( verbosity ){
+ perror("XPAMainLoop() select");
+ }
+ exit(1);
+ }
+ /* can't happen, since we have no timeout */
+ else if( sgot == 0 )
+ ;
+ /* finally ... something to do */
+ else
+ got += XPAProcessSelect(&readfds, 0);
+ FD_ZERO(&readfds);
+ }
+ return(got);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPASleep
+ *
+ * Purpose: sleep for specified milliseconds
+ *
+ * Returns: none
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+void
+XPASleep (int msec)
+#else
+void XPASleep(msec)
+ int msec;
+#endif
+{
+ struct timeval tv;
+
+ if( msec > 0 ){
+ tv.tv_sec = msec / 1000;
+ tv.tv_usec = (msec % 1000) * 1000;
+ xselect(1, NULL, NULL, NULL, &tv);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPAListHead
+ *
+ * Purpose: semi-public routine to return the head of the xpa list
+ *
+ * Results: XPA list pointer on success
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPAMtype (void)
+#else
+int XPAMtype()
+#endif
+{
+ return(mtype);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPATmpdir
+ *
+ * Purpose: semi-public routine to return the tmpdir value
+ *
+ * Results: tmpdir
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+char *
+XPATmpdir (void)
+#else
+char * XPATmpdir()
+#endif
+{
+ return(tmpdir);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPASetBuf
+ *
+ * Purpose: set the return buffer in a server send callback
+ * (use mainly by tcl to transfer tcl buffers to C)
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPASetBuf (XPA xpa, char *buf, size_t len, int xcopy)
+#else
+int XPASetBuf(xpa, buf, len, xcopy)
+ XPA xpa;
+ char *buf;
+ size_t len;
+ int xcopy;
+#endif
+{
+ /* sanity check */
+ if( !xpa || !xpa->comm )
+ return(-1);
+ /* xcopy >0 => make a copy of the data, else just assign */
+ if( xcopy ){
+ xpa->comm->len = len;
+ if( (xpa->comm->buf = (char *)xmalloc(len)) == NULL )
+ return(-1);
+ memcpy(xpa->comm->buf, buf, len);
+ }
+ else{
+ xpa->comm->len = len;
+ xpa->comm->buf = buf;
+ }
+ return(0);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPASetBuf
+ *
+ * Purpose: set the return buffer in a server send callback
+ * (use mainly by tcl to transfer tcl buffers to C)
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+int
+XPASetFree (XPA xpa, MyFree myfree, void *myfree_ptr)
+#else
+ int XPASetFree(xpa, myfree, myfree_ptr)
+ XPA xpa;
+ MyFree myfree;
+ void *myfree_ptr;
+#endif
+{
+ /* sanity check */
+ if( !xpa || !xpa->comm ) return(-1);
+ /* set the free routine and data pointer */
+ xpa->comm->myfree = myfree;
+ xpa->comm->myfree_ptr = myfree_ptr;
+ return(0);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: XPACleanup
+ *
+ * Purpose: make valgrind happy by freeing memory
+ *
+ * Results: none
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+void XPACleanup (void)
+#else
+void XPACleanup ()
+#endif
+{
+ XPAFreeReserved();
+ XPAAclFree();
+ if( tmpdir ){
+ xfree(tmpdir);
+ tmpdir = NULL;
+ }
+}
+
+#ifdef ANSI_FUNC
+void XPASaveJmp(void *env)
+#else
+void XPASaveJmp(env)
+ void *env;
+#endif
+{
+#if HAVE_SETJMP_H
+ xalloc_savejmp((jmp_buf *)env);
+#else
+ return;
+#endif
+}