diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2018-01-23 16:54:55 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2018-01-23 16:54:55 (GMT) |
commit | 5bf617b1e9347322b8b16419fd5e74107e8b1c26 (patch) | |
tree | e5bbb785b82b35bc62df41bebca8b540078aaf76 /xpa/client.c | |
parent | 51e1f85047b34f095ed69a3024d696997d2667c8 (diff) | |
parent | 0ebcd152c10d2eae6b62f16e7138aa187a8a1bdd (diff) | |
download | blt-5bf617b1e9347322b8b16419fd5e74107e8b1c26.zip blt-5bf617b1e9347322b8b16419fd5e74107e8b1c26.tar.gz blt-5bf617b1e9347322b8b16419fd5e74107e8b1c26.tar.bz2 |
Merge commit '0ebcd152c10d2eae6b62f16e7138aa187a8a1bdd' as 'xpa'
Diffstat (limited to 'xpa/client.c')
-rw-r--r-- | xpa/client.c | 3057 |
1 files changed, 3057 insertions, 0 deletions
diff --git a/xpa/client.c b/xpa/client.c new file mode 100644 index 0000000..f70ff40 --- /dev/null +++ b/xpa/client.c @@ -0,0 +1,3057 @@ +/* + * Copyright (c) 1999-2003 Smithsonian Astrophysical Observatory + */ + +#include <xpap.h> + +/* + *---------------------------------------------------------------------------- + * + * + * Private Routines and Data + * + * + *---------------------------------------------------------------------------- + */ + +/* this is the head of the global list of client xpas */ +static XPA xpaclienthead=NULL; + +static char errbuf[SZ_LINE]; /* holds current error message */ +static int id=0; /* id of current command */ + +#define DATA_CONNECT 1 +#define DATA_ACCEPT 2 +#define DATA_DATA 4 + +/* use of a double fork() call is used to prevent zombies which happen + if fork is a child of xpans started by an XPA server (i.e., for some + reason, the SIGCHLD signal does not get sent to xpans parent) + See Stevens, Advanced Programming in te Unix Environment, p. 202 */ +#define USE_DOUBLE_FORK 1 +#ifndef USE_DOUBLE_FORK +#ifdef ANSI_FUNC +void sig_chld(int signo) +#else +#endif +{ + int stat; + + while(waitpid(-1, &stat, WNOHANG) > 0){ + ; + } + return; +} +#endif + +/* + *---------------------------------------------------------------------------- + * + * Routine: rdl + * + * Purpose: read characters up a new-line + * + * Returns: number of characters read + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +static int +rdl (int fd, char *buf, size_t len) +#else +static int rdl(fd, buf, len) + int fd; + char *buf; + int len; +#endif +{ + int i=0; + int got; + + /* start out clean */ + *buf = '\0'; + + /* make sure we have a valid channel */ + if( fd < 0 ) + return(-1); + + /* grab characters up to a new-line or max len */ + while( i < (len-1) ){ + got = read(fd, &(buf[i]), 1); + if( got < 1 ) + break; + else if( buf[i++] == '\n' ) + break; + } + buf[i] = '\0'; + return(i); +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: XPAProxyAccept + * + * Purpose: accept a connection from an XPA proxy server + * + * Return: fd of accepted connection or -1 + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +static +int XPAProxyAccept(XPA xpa, char *method, char *xclass, char *name, int ifd, + unsigned int *rip, unsigned short *rport, char *rname) +#else +static +int XPAProxyAccept(xpa, method, xclass, name, ifd, rip, rport, rname) + XPA xpa; + char *method; + char *xclass; + char *name; + int ifd; + unsigned int *rip; + unsigned short *rport; + char *rname; +#endif +{ + int sock; + int got; + int oum; + int ofd; + int niter; + int swidth=FD_SETSIZE; + int keep_alive=1; + int reuse_addr=1; + unsigned int ip; + unsigned short port; + char tbuf[SZ_LINE]; + char amethod[SZ_LINE]; + char *tptr; + socklen_t slen; + struct sockaddr_in sock_in; +#if HAVE_SYS_UN_H + struct sockaddr_un sock_un; +#endif + struct timeval tv; + struct timeval *tvp; + fd_set readfds; + + /* initialize results */ + if( rip ) *rip = 0; + if( rport ) *rport = 0; + if( rname ) *rname = '\0'; + + switch(XPAMethod(method)){ + case XPA_INET: + if( !XPAParseIpPort(method, &ip, &port) ){ + goto error; + } + /* open a socket for data connections */ + if( (sock = xsocket(AF_INET, SOCK_STREAM, 0)) < 0 ){ + PERROR(("xpaaccept socket")); + goto error; + } + setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, + (char *)&keep_alive, sizeof(keep_alive)); + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + (char *)&reuse_addr, sizeof(reuse_addr)); + memset((char *)&sock_in, 0, sizeof(sock_in)); + sock_in.sin_family = AF_INET; + sock_in.sin_addr.s_addr = htonl(INADDR_ANY); + sock_in.sin_port = htons(port); + /* bind to the ip:port */ + if( xbind(sock, (struct sockaddr *)&sock_in, sizeof(sock_in)) < 0 ){ + PERROR(("xpaaccept bind")); + xclose(sock); + goto error; + } + snprintf(amethod, SZ_LINE, "%x:%d", ip, port); + break; +#if HAVE_SYS_UN_H + case XPA_UNIX: + ip = 0; + port = 0; + /* get filename part, composed of class and name and unique id */ + snprintf(tbuf, SZ_LINE, "%s_%s.%d", xclass, name, (int)time(NULL)); + /* change "/" to "_" for filename */ + for(tptr = tbuf; *tptr != '\0'; tptr++){ + if( *tptr == '/' ) *tptr = '_'; + } + /* create full pathname */ + snprintf(amethod, SZ_LINE, "%s/%s", XPATmpdir(), tbuf); + /* delete old copy */ + unlink (amethod); + /* open a socket and fill in socket information */ + if( (sock = xsocket(AF_UNIX, SOCK_STREAM, 0)) < 0 ){ + goto error; + } + setsockopt(sock, 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, amethod); + /* unset umask so that everyone can read and write */ + oum = umask(0); + /* bind to the file */ + got = xbind(sock, (struct sockaddr *)&sock_un, sizeof(sock_un)); + /* reset umask to previous */ + umask(oum); + /* now check for bind error */ + if( got < 0 ){ + xclose(sock); + goto error; + } + break; +#endif + default: + goto error; + } + + /* send port to client so they can connect */ + /* first listen for the connection */ + if( listen(sock, XPA_MAXLISTEN) < 0 ){ + PERROR(("xpaaccept listen")); + xclose(sock); + goto error; + } + + /* and tell the client that we are listening */ + snprintf(tbuf, SZ_LINE, "xpaaccept %s (%s:%s %s)\n", + amethod, xclass, name, method); + FPRINTF((stderr, "%sXPAProxyAccept: sending command to %d:\n%s", + _sp, ifd, tbuf)); + if( XPAPuts(NULL, ifd, tbuf, XPAShortTimeout()) <= 0 ){ + PERROR(("client xpaaccept write")); + xclose(sock); + goto error; + } + + /* we will iterate on xselect */ + if( XPAShortTimeout() > 0 ) + niter = XPAShortTimeout()*100; + else + niter = XPA_SHORT_TIMEOUT*100; +again: + /* this has to be able to time out */ + tv.tv_sec = 0; + tv.tv_usec = 10000; + tvp = &tv; + /* wait for this socket and XPA sockets */ + FD_ZERO(&readfds); + FD_SET(sock, &readfds); + XPAAddSelect(NULL, &readfds); + /* wait for the connection */ + got = xselect(swidth, &readfds, NULL, NULL, tvp); + /* process results of select */ + if( got > 0 ){ + if( !FD_ISSET(sock, &readfds)){ + XPAProcessSelect(&readfds, 0); + goto again; + } + switch(XPAMethod(method)){ + case XPA_INET: + while( 1 ){ + slen = sizeof(struct sockaddr_in); + if((ofd=xaccept(sock, (struct sockaddr *)&sock_in, &slen)) >= 0){ + break; + } + else{ + if( xerrno == EINTR ) + continue; + else{ + PERROR(("xpaaccept acccept")); + xclose(sock); + goto error; + } + } + } + break; +#if HAVE_SYS_UN_H + case XPA_UNIX: + while( 1 ){ + slen = sizeof(struct sockaddr_un); + if((ofd=xaccept(sock, (struct sockaddr *)&sock_un, &slen)) >= 0){ + break; + } + else{ + if( xerrno == EINTR ) + continue; + else{ + PERROR(("xpaaccept acccept")); + xclose(sock); + goto error; + } + } + } + break; +#endif + default: + xclose(sock); + goto error; + } + } + /* timeout? */ + else if( got == 0 ){ + if( --niter > 0 ){ + goto again; + } + else{ + xclose(sock); + FPRINTF((stderr, "%sXPAProxyAccept: select timed out\n", _sp)); + goto error; + } + } + /* error */ + else{ + if( xerrno == EINTR ){ + PERROR(("xpaaccept select")); + goto again; + } + else{ + xclose(sock); + goto error; + } + } + /* done with listening */ + xclose(sock); + + /* fill in return information */ + if( rip ) *rip = ip; + if( rport ) *rport = port; + if( rname ){ + strncpy(rname, amethod, SZ_LINE-1); + rname[SZ_LINE-1] = '\0'; + } + return(ofd); + +error: + return(-1); +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: XPAClientNewInput + * + * Purpose: allocate a new input struct for reading data from stdin + * + * Return: input struct, or NULL on error + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +static XPAInput +XPAClientNewInput(XPA xpa) +#else +static XPAInput XPAClientNewInput(xpa) + XPA xpa; +#endif +{ + XPAInput xnew, inp; + + /* allocate a new record */ + if( (xnew=(XPAInput)xcalloc(1, sizeof(XPAInputRec))) == NULL ){ + return(NULL); + } + /* allocate the data buffer */ + xnew->buf = (char *)xmalloc(XPA_BIOSIZE); + /* this buffer starts (and currently ends) at the current byte count */ + xnew->start = xpa->inpbytes; + xnew->end = xpa->inpbytes; + xnew->bytes = 0; + + /* add this input to end of list of input's */ + if( xpa->inphead == NULL ){ + xpa->inphead = xnew; + } + else{ + for(inp=xpa->inphead; inp->next!=NULL; inp=inp->next) + ; + inp->next = xnew; + } + + /* return the record struct */ + return(xnew); +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: XPAClientFreeInput + * + * Purpose: free a input buffer once its been sent to all targets + * + * Return: 0 on success, -1 on failure + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +static void +XPAClientFreeInput (XPA xpa, XPAInput inp) +#else +static void XPAClientFreeInput(xpa, inp) + XPA xpa; + XPAInput inp; +#endif +{ + XPAInput cur; + + if( !xpa || !inp ) + return; + + if( inp == xpa->inphead ){ + xpa->inphead = inp->next; + } + else{ + for(cur=xpa->inphead; cur!=NULL; cur=cur->next){ + if( cur->next == inp ){ + cur->next = inp->next; + break; + } + } + } + + /* free current record */ + if( inp != NULL ){ + if( inp->buf != NULL ) + xfree(inp->buf ); + xfree(inp); + } +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: XPAClientFreeAllInputs + * + * Purpose: free remaining input buffers + * + * Return: 0 on success, -1 on failure + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +static void +XPAClientFreeAllInputs (XPA xpa) +#else +static void XPAClientFreeAllInputs(xpa) + XPA xpa; +#endif +{ + XPAInput cur, tmp; + + if( !xpa ) + return; + + for(cur=xpa->inphead; cur!=NULL; ){ + tmp = cur->next; + XPAClientFreeInput(xpa, cur); + cur = tmp; + } + xpa->inpbytes = 0; +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: XPAClientProcessInput + * + * Purpose: read input from stdin and store in an input struct + * + * Return: bytes read + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +static int +XPAClientProcessInput(XPA xpa) +#else +static int XPAClientProcessInput(xpa) + XPA xpa; +#endif +{ + static XPAInput cur=NULL; + int get, got; + + /* set up next buffer, if necessary */ + for(cur=xpa->inphead; cur!=NULL; cur=cur->next){ + if( cur->bytes < XPA_BIOSIZE ) + break; + } + if( cur == NULL ){ + cur = XPAClientNewInput(xpa); + } + + /* read data from stdin */ + get = MIN(XPA_IOSIZE, XPA_BIOSIZE - cur->bytes); + if( isatty(xpa->ifd) ){ + got = rdl(xpa->ifd, &(cur->buf[cur->bytes]), get); + } + else{ + got = read(xpa->ifd, &(cur->buf[cur->bytes]), get); + } + switch(got){ + case -1: + if( XPAVerbosity() ){ + PERROR(("XPA client read")); + } + return(0); + case 0: + xpa->ifd = -1; + FPRINTF((stderr, "%sXPAClientProcessInput: signalling EOF\n", _sp)); + break; + default: + break; + } + cur->bytes += got; + cur->end += got; + xpa->inpbytes += got; +#ifdef FIXEDBYCYGWIN +#if HAVE_CYGWIN + /* on non-NT Windows machines, Cygwin select() does not work once a pipe + gets EOF. It should show the fd ready for reading (and read 0 bytes), + but does not, so we have to hack a manual check */ + /* GetVersion is a Windows call */ + if( GetVersion() >= 0x80000000L ){ + if( got < get ){ + xpa->ifd = -1; + } + } +#endif +#endif + + /* verify to stdout, if necessary */ + if( xpa->client_mode & XPA_CLIENT_VERIFY ){ + fwrite(&(cur->buf[cur->bytes-got]), sizeof(char), got, stdout); + } + + /* return the number of bytes just read */ + return(got); +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: XPAClientFree + * + * Purpose: free a client record and remove from list + * + * Returns: none + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +static void +XPAClientFree (XPA xpa, XPAClient client) +#else +static void XPAClientFree(xpa, client) + XPA xpa; + XPAClient client; +#endif +{ + XPAClient cur; + + /* remove from list of xpa's */ + if( xpa->clienthead ){ + if( xpa->clienthead == client ){ + xpa->clienthead = client->next; + } + else{ + for(cur=xpa->clienthead; cur!=NULL; cur=cur->next){ + if( cur->next == client ){ + cur->next = client->next; + break; + } + } + } + } + + if( client->cmdfd >= 0 ){ +#if HAVE_CYGWIN + shutdown(client->cmdfd, SHUT_RDWR); +#endif + xclose(client->cmdfd); + } + if( client->datafd >= 0 ){ +#if HAVE_CYGWIN + shutdown(client->datafd, SHUT_RDWR); +#endif + xclose(client->datafd); + } + if( client->dataname ){ + unlink(client->dataname); + xfree(client->dataname); + } + if( client->method ) + xfree(client->method); + if( client->info ) + xfree(client->info); + if( client->xtemplate ) + xfree(client->xtemplate); + if( client->xclass ) + xfree(client->xclass); + if( client->name ) + xfree(client->name); + if( client->id ) + xfree(client->id); + /* xpaget's fd mode has an alloc'ed bufptr and lenptr */ + if( (client->type == 'g') && (client->mode & XPA_CLIENT_FD) ){ + if( client->bufptr && *(client->bufptr) ) + xfree(*(client->bufptr)); + if( client->bufptr ) + xfree(client->bufptr); + if( client->lenptr ) + xfree(client->lenptr); + } + xfree(client); +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: XPAClientDataSent + * + * Purpose: data is sent, so close data channel and change status to + * signal that we are waiting for the server + * + * Returns: none + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +static void +XPAClientDataSent (XPA xpa, XPAClient client) +#else +static void XPAClientDataSent(xpa, client) + XPA xpa; + XPAClient client; +#endif +{ + FPRINTF((stderr, "%sXPAClientDataSent: for cmd %d data %d\n", _sp, + client->cmdfd, client->datafd)); + /* close the data channel, which should trigger a result from the server */ + if( client->datafd >= 0 ){ +#if HAVE_CYGWIN + shutdown(client->datafd, SHUT_RDWR); +#endif + xclose(client->datafd); + client->datafd = -1; + } + /* we are now waiting for the server to complete the calllback */ + client->status = XPA_CLIENT_WAITING; +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: XPAClientEnd + * + * Purpose: finish up with this client + * + * Returns: error message or null + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +static char * +XPAClientEnd (XPA xpa, XPAClient client) +#else +static char *XPAClientEnd(xpa, client) + XPA xpa; + XPAClient client; +#endif +{ + char *error=NULL; + char *eptr; + + FPRINTF((stderr, "%sXPAClientEnd: for cmd %d data %d\n", _sp, + client->cmdfd, client->datafd)); + /* always read the status line -- if we are not ack'ing, we'll get an + OK from the server before the calllback and we can exit quickly */ + /* don't do this if client is xpainfo and we're not ack'ing */ + if( !((client->type == 'i') && !(client->mode & XPA_CLIENT_ACK)) ){ +retry: + if( XPAGets(NULL, client->cmdfd, errbuf, SZ_LINE, XPALongTimeout()) >0 ){ + FPRINTF((stderr, "%sXPAClientEnd: read %s\n", _sp, errbuf)); + eptr = errbuf; + /* this should never happen */ + if( *eptr == '?' ){ + snprintf(errbuf, SZ_LINE, + "XPA$WARNING: protocol mismatch - missing id\n%s", eptr); + error = NULL; + } + else{ + /* make sure we are dealing with a proper message */ + if( strncmp(eptr, client->id, strlen(client->id)) ){ + if( XPAVerbosity() > 1 ){ + fprintf(stderr, + "XPA$WARNING: ignoring out of sync server message:\n"); + fprintf(stderr, "%s", errbuf); + } + goto retry; + } + /* go past id */ + eptr += strlen(client->id); + while( isspace((int)*eptr) ) eptr++; + if( !strncmp(eptr, "XPA$OK", 6) ){ + error = NULL; + } + else{ + error = eptr; + } + } + } + else{ + if( XPAVerbosity() > 1 ){ + fprintf(stderr, + "XPA$WARNING: no reply from server callback (assuming OK)\n"); + } + error = NULL; + } + } + else + error = NULL; + + /* store the error return */ + if( client->errptr ) + *(client->errptr) = xstrdup(error); + + /* remove this client if we are not meant to persist */ + if( !xpa->persist ){ + XPAClientFree(xpa, client); + } + /* otherwise mark as inactive */ + else{ + client->status = XPA_CLIENT_IDLE; + client->bytes = 0; + } + + /* return error status */ + return(error); +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: XPAClientNew + * + * Purpose: allocate a new xpa client + * + * Returns: xpa client struct + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +static XPAClient +XPAClientNew (XPA xpa, char *mode, char *xtemplate, int type, + char *xclass, char *name, char *method, char *info) +#else +static XPAClient XPAClientNew(xpa, mode, xtemplate, type, + xclass, name, method, info) + XPA xpa; + char *mode; + char *xtemplate; + int type; + char *xclass; + char *name; + char *method; + char *info; +#endif +{ + XPAClient xnew, client; + struct sockaddr_in sock_in; +#if HAVE_SYS_UN_H + struct sockaddr_un sock_un; +#endif + char xmode[SZ_LINE]; + char tbuf[SZ_LINE]; + char amethod[SZ_LINE]; + char *s=NULL; + unsigned short port; + unsigned int ip=0; + int fd; + int pfd; + int tries=0; + int nsproxy=0; + int keep_alive=1; + + FPRINTF((stderr, "%sXPAClientNew: entering with %s %s %s %s\n", _sp, + xclass, name, method, info)); + + /* no errors as yet */ + *errbuf = '\0'; + + /* look for reuse of xpans fd (used in conjunction with the xpans proxy) */ + *xmode = '\0'; + if( mode ){ + strncpy(xmode, mode, SZ_LINE-1); + xmode[SZ_LINE-1] = '\0'; + } + if( keyword(xmode, "nsproxy", tbuf, SZ_LINE) ){ + nsproxy = 1; + pfd = strtol(tbuf, &s, 0); + fd = XPAProxyAccept(xpa, XPANSMethod(NULL,2), + xclass, name, pfd, &ip, &port, amethod); + /* make sure we got a valid int fd */ + if( fd < 0 ){ + snprintf(errbuf, SZ_LINE, + "XPA$ERROR: no response from server on proxyaccept (%s:%s%s)\n", + xclass, name, XPATimestamp()); + FPRINTF((stderr, "%sXPAClientNew: %s", _sp, errbuf)); + PERROR(("XPAClientNew")); + return(NULL); + } + } + /* normal usage: connect to server */ + else{ + switch(XPAMethod(method)){ + case XPA_INET: +again1: + if( !XPAParseIpPort(method, &ip, &port) ) + return(NULL); + /* use $localhost over $host (we do not trust host to be correct) */ + if( (ip == gethostip("$host")) && (tries == 0) ) + ip = gethostip("$localhost"); + /* connect to the server before we go further */ + if( (fd = xsocket(AF_INET, SOCK_STREAM, 0)) < 0 ){ + return(NULL); + } + setsockopt(fd, 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(fd, (struct sockaddr *)&sock_in, sizeof(sock_in)) <0 ){ + xclose(fd); + /* 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{ + snprintf(errbuf, SZ_LINE, + "XPA$ERROR: no response from server on connect (%s:%s%s)\n", + xclass, name, XPATimestamp()); + PERROR(("XPAClientNew")); + return(NULL); + } + } + /* make sure we close on exec */ + xfcntl(fd, F_SETFD, FD_CLOEXEC); + FPRINTF((stderr, "%sXPAClientNew: inet connect returns fd %d\n", + _sp, fd)); + break; +#if HAVE_SYS_UN_H + case XPA_UNIX: +again2: + /* open a socket and fill in socket information */ + if( (fd = xsocket(AF_UNIX, SOCK_STREAM, 0)) < 0 ){ + return(NULL); + } + setsockopt(fd, 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(fd, (struct sockaddr *)&sock_un, sizeof(sock_un)) <0 ){ + xclose(fd); + /* 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 < XPA_RETRIES) ){ + tries++; + XPASleep(10); + goto again2; + } + /* give up */ + else{ + snprintf(errbuf, SZ_LINE, + "XPA$ERROR: no response from server on connect (%s:%s%s)\n", + xclass, name, XPATimestamp()); + PERROR(("XPAClientNew")); + return(NULL); + } + } + /* make sure we close on exec */ + xfcntl(fd, F_SETFD, FD_CLOEXEC); + FPRINTF((stderr, "%sXPAClientNew: unix connect returns fd %d\n", + _sp, fd)); + break; +#endif + default: + return(NULL); + } + strncpy(amethod, method, SZ_LINE-1); + amethod[SZ_LINE-1] = '\0'; + } + + /* allocate new send record */ + if( (xnew=(XPAClient)xcalloc(1, sizeof(XPAClientRec))) == NULL ){ + xclose(fd); + return(NULL); + } + + /* fill in the blanks */ + xnew->xtemplate = xstrdup(xtemplate); + xnew->type = type; + xnew->cmdfd = fd; + xnew->datafd = -1; + xnew->xclass = xstrdup(xclass); + xnew->name = xstrdup(name); + xnew->method = xstrdup(amethod); + xnew->info = xstrdup(info); + xnew->ip = ip; + xnew->nsproxy = nsproxy; + xnew->status = XPA_CLIENT_ACTIVE; + + /* now that we have a valid client, add to list */ + if( xpa->clienthead == NULL ){ + xpa->clienthead = xnew; + } + else{ + for(client=xpa->clienthead; client->next!=NULL; client=client->next) + ; + client->next = xnew; + } + FPRINTF((stderr, "%sXPAClientNew: new fd %d\n", _sp, xnew->cmdfd)); + /* return the good news */ + return(xnew); +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: XPAClientAddSelect + * + * Purpose: add one or more xpa client sockets to the select flags + * + * Return: number of clients that were added to the select flags + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +int +XPAClientAddSelect (XPA xpa, fd_set *readfdsptr, fd_set *writefdsptr) +#else +int XPAClientAddSelect(xpa, readfdsptr, writefdsptr) + XPA xpa; + fd_set *readfdsptr; + fd_set *writefdsptr; +#endif +{ + XPAClient client; + int got=0; + int loop=0; + + /* better have some place to set the flags */ + if( readfdsptr == NULL ) + return(0); + + /* if no xpa is specified, do them all */ + if( xpa == NULL ){ + if( xpaclienthead == NULL ) return(0); + xpa = xpaclienthead; + loop = 1; + } + +loop: + /* set select flags for all clients */ + for(client=xpa->clienthead; client!=NULL; client=client->next){ + /* if this client is processing */ + if( (client->status == XPA_CLIENT_PROCESSING) && (client->datafd >= 0) ){ + if( client->type == 'g' ){ + FPRINTF((stderr, "%sXPAClientAddSelect(get): adding fd %d\n", _sp, + client->datafd)); + FD_SET(client->datafd, readfdsptr); + got++; + } + else if( client->type == 's' ){ + FPRINTF((stderr, "%sXPAClientAddSelect(set): adding fd %d\n", _sp, + client->datafd)); + FD_SET(client->datafd, writefdsptr); + got++; + } + } + /* if this client is waiting */ + else if( (client->status == XPA_CLIENT_WAITING) && (client->cmdfd >= 0) ){ + FPRINTF((stderr, "%sXPAClientAddSelect(waiting): adding fd %d\n", _sp, + client->cmdfd)); + FD_SET(client->cmdfd, readfdsptr); + got++; + } + } + /* loop if necessary */ + if( loop && (xpa=xpa->next) ) goto loop; + /* return the news */ + return(got); +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: XPAClientGet + * + * Purpose: process an xpaget request for a given client + * + * Return: 0 on success, -1 on failure + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +static int +XPAClientGet (XPA xpa, XPAClient client) +#else +static int XPAClientGet(xpa, client) + XPA xpa; + XPAClient client; +#endif +{ + int status; + char tbuf[SZ_LINE]; + + /* allocate the first buffer, if necessary */ + if( *(client->bufptr) == NULL ){ + client->bufsize = XPA_IOSIZE; + *(client->bufptr) = (char *)xmalloc(client->bufsize); + *(client->lenptr) = 0; + } + if( (*(client->lenptr) + XPA_IOSIZE) > client->bufsize ){ + client->bufsize += (XPA_IOSIZE*10); + *(client->bufptr) = (char *)xrealloc(*(client->bufptr), client->bufsize); + } + + /* now retrieve the data from the server */ + status = recv(client->datafd, *(client->bufptr) + *(client->lenptr), + XPA_IOSIZE, 0); + /* status < 0 means error */ + switch(status){ + /* error */ + case -1: + /* socket would block */ + if((xerrno == EINPROGRESS) || (xerrno == EWOULDBLOCK)){ + return(0); + } + /* clean up after error */ + if( *(client->bufptr) ){ + xfree(*(client->bufptr)); + *(client->bufptr) = NULL; + client->bufsize = 0; + } + *(client->lenptr) = 0; + XPAClientDataSent(xpa, client); +#ifdef OLD + (void)XPAClientEnd(xpa, client); + /* we need to flag some sort of error, if nothing came across */ + if( *(client->errptr) == NULL ){ + *(client->errptr) = xstrdup( + "XPA$ERROR: incomplete transmission from server\n"); + } +#endif + break; + /* eof */ + case 0: + /* if we have multiple clients, we now need to write this one */ + if( client->mode & XPA_CLIENT_FD ){ + if( xpa->nclient > 1 ){ + snprintf(tbuf, SZ_LINE, "XPA$BEGIN %s:%s %s\n", + client->xclass, client->name, client->method); + write(client->fd, tbuf, strlen(tbuf)); + } + write(client->fd, *(client->bufptr), *(client->lenptr)); + if( xpa->nclient > 1 ){ + snprintf(tbuf, SZ_LINE, "XPA$END %s:%s %s\n", + client->xclass, client->name, client->method); + write(client->fd, tbuf, strlen(tbuf)); + } + /* we can free buf, since its not being passed back */ + if( *(client->bufptr) ){ + xfree(*(client->bufptr)); + *(client->bufptr) = NULL; + client->bufsize = 0; + } + } + else{ + /* set final buffer size and put a convenience null at the end */ + if( *(client->bufptr) ){ + client->bufsize = *(client->lenptr)+1; + *(client->bufptr) = (char *)xrealloc(*(client->bufptr),client->bufsize); + *(*(client->bufptr)+*(client->lenptr)) = '\0'; + } + } + /* for all clients, we need to clean up */ + XPAClientDataSent(xpa, client); +#ifdef OLD + (void)XPAClientEnd(xpa, client); +#endif + break; + /* status > 0: bytes read */ + default: + *(client->lenptr) += status; + /* for single client fd mode, we write immediately -- this deals with + the important case of one client with a large amount of data */ + if( (client->mode & XPA_CLIENT_FD) && (xpa->nclient == 1) ){ + write(client->fd, *(client->bufptr), *(client->lenptr)); + /* reset buf for next read */ + if( *(client->bufptr) ) xfree(*(client->bufptr)); + *(client->bufptr) = NULL; + *(client->lenptr) = 0; + } + break; + } + return(status); +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: XPAClientSet + * + * Purpose: process an xpaset request for a given client + * + * Return: 0 on success, -1 on failure + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +static int +XPAClientSet (XPA xpa, XPAClient client) +#else +static int XPAClientSet(xpa, client) + XPA xpa; + XPAClient client; +#endif +{ + int status; + int left; + int got; + int len; + XPAInput inp; + + if( client->mode & XPA_CLIENT_BUF ){ + while( 1 ){ + len = MIN(XPA_IOSIZE, client->len - client->bytes); + /* see if we have written it all */ + if( len == 0 ){ + status = 1; + goto done; + } + /* write the next chunk */ + FPRINTF((stderr, + "%sXPAClientSet: fd %d sending %lu bytes (%lu - %lu)\n", _sp, + client->datafd, (unsigned long)len, + (unsigned long)client->len, (unsigned long)client->bytes)); + got=send(client->datafd, &(client->buf[client->bytes]), len, 0); + if( got >= 0 ){ + client->bytes += got; + if( XPALevelGet() >0 ) + return(got); + } + else{ + PERROR(("XPAClientSet")); + /* check for error */ + if( (xerrno != EWOULDBLOCK) && (xerrno != EAGAIN) ){ + status = -1; + goto done; + } + /* write would block, so we return and wait the server */ + else{ + return(0); + } + } + } + } + /* reading from stdin and writing to servers */ + else{ + /* find the input buf that contains the data we need */ + for(inp=xpa->inphead; inp!=NULL; inp=inp->next){ + if( (client->bytes >= inp->start) && (client->bytes < inp->end) ){ + break; + } + } + /* if we can't find a buffer ... */ + if( !inp ){ + /* ... and we have all the input, we are done */ + if( xpa->ifd < 0 ){ + FPRINTF((stderr, "%sXPAClientSet: all data read\n", _sp)); + status = 1; + goto done; + } + /* ... but there is more input to come, return */ + else{ + return(0); + } + } + /* optimization: don't write a buffer until its full (or until eof) */ + if( (xpa->ifd >=0) && (inp->bytes < XPA_BIOSIZE) ){ + return(0); + } + + /* write bytes until we would block or until end of this buffer, etc */ + while( 1 ){ + len = MIN(XPA_IOSIZE, inp->end - client->bytes); + FPRINTF((stderr, "%sXPAClientSet: has %lu=min(%d,(%lu-%lu)) [%d]\n", + _sp, (unsigned long)len, XPA_IOSIZE, + (unsigned long)inp->end, (unsigned long)client->bytes, + client->status)); + /* if we are done with this buffer, just return */ + if( (client->status == XPA_CLIENT_PROCESSING) && (len <=0) ){ + /* see if everyone else is done with this buffer as well, + in which case we can free it */ + left = 0; + for(client=xpa->clienthead; client!=NULL; client=client->next){ + if( (client->type != 's') || !(client->mode & XPA_CLIENT_FD) ) + continue; + /* in order to be totally written out, the following must be true: + * 1. send->bytes must be past the end of this buffer + * and + * 2. this buffer must be filled or else we hit eof + */ + FPRINTF((stderr, + "%sXPAClientSet: %lu>=%lu && ((%lu>=%d) or %d<0)) .. ", + _sp, client->bytes, (unsigned long)inp->end, + (unsigned long)inp->bytes, XPA_BIOSIZE, + xpa->ifd)); + if( (client->bytes >= inp->end) && + ((inp->bytes >= XPA_BIOSIZE) || (xpa->ifd < 0)) ){ + /* buffer complete written */ + FPRINTF((stderr, "%sEOF (%lu)\n", + _sp, (unsigned long)xpa->inpbytes)); + ; + } + else{ + FPRINTF((stderr, "%s\n", _sp)); + /* buffer not complete written */ + left++; + break; + } + } + /* if nothing is left, we can free this input struct */ + if( !left ){ + XPAClientFreeInput(xpa, inp); + } + return(0); + } + /* write to the server */ + FPRINTF((stderr, + "%sXPAClientSet: fd %d sending %lu bytes (%lu):\n", _sp, + client->datafd, + (unsigned long)len, (unsigned long)client->bytes)); + got = send(client->datafd, &(inp->buf[client->bytes-inp->start]),len,0); + /* check for success */ + if( got >= 0 ){ + /* update the number of bytes we wrote */ + client->bytes += got; + FPRINTF((stderr, "%sXPAClientSet: sent %lu bytes (total is %lu)\n", + _sp, (unsigned long)got, (unsigned long)client->bytes)); + /* go back for more */ + if( XPALevelGet() >0 ) + return(got); + else + continue; + } + /* check for error */ + else{ + PERROR(("XPAClientSet")); + /* anything but a "would block" error is bad */ + if( (xerrno != EWOULDBLOCK) && (xerrno != EAGAIN) ){ + status = -1; + goto done; + } + /* write would block, so we return and wait the server */ + else{ + FPRINTF((stderr, "%sXPAClientSet: waiting for more data\n", _sp)); + return(0); + } + } + } + } + +done: + XPAClientDataSent(xpa, client); +#ifdef OLD + (void)XPAClientEnd(xpa, client); +#endif + return(status); +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: XPAClientProcessSelect + * + * Purpose: process xpas that have pending reads or writes + * + * Return: number of xpas processed + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +int +XPAClientProcessSelect (XPA xpa, + fd_set *readfdsptr, fd_set *writefdsptr, int maxreq) +#else +int XPAClientProcessSelect(xpa, readfdsptr, writefdsptr, maxreq) + XPA xpa; + fd_set *readfdsptr; + fd_set *writefdsptr; + int maxreq; +#endif +{ + int got=0; + int loop=0; + XPAClient client; + + /* <= 0 means do all of them */ + if( maxreq < 0 ){ + maxreq = 0; + } + + /* if no xpa is specified, do them all */ + if( xpa == NULL ){ + if( xpaclienthead == NULL ) return(0); + xpa = xpaclienthead; + loop = 1; + } + +loop: + /* first process any new input before we write output */ + if( xfd_isset_stdin(xpa->ifd, readfdsptr) ){ + xfd_clr_stdin(xpa->ifd, readfdsptr); + XPAClientProcessInput(xpa); + } + /* look at all clients */ +again: + for(client=xpa->clienthead; client!=NULL; client=client->next){ + /* if we are processing */ + if( (client->status == XPA_CLIENT_PROCESSING) && (client->datafd>=0) ){ + /* then handle new requests */ + if((client->type == 'g') && FD_ISSET(client->datafd, readfdsptr)){ + FD_CLR(client->datafd, readfdsptr); + XPAClientGet(xpa, client); + got++; + if( maxreq && (got >= maxreq) ) return(got); + goto again; + } + else if((client->type == 's') && FD_ISSET(client->datafd, writefdsptr)){ + FD_CLR(client->datafd, writefdsptr); + /* if the return is > 0, we completed the send */ + if( XPAClientSet(xpa, client) > 0 ) + got++; + if( maxreq && (got >= maxreq) ) return(got); + goto again; + } + } + /* if this client is waiting */ + else if( (client->status == XPA_CLIENT_WAITING) && (client->cmdfd >= 0) ){ + if( FD_ISSET(client->cmdfd, readfdsptr)){ + FD_CLR(client->cmdfd, readfdsptr); + XPAClientEnd(xpa, client); + got++; + if( maxreq && (got >= maxreq) ) return(got); + goto again; + } + } + } + /* loop if necessary */ + if( loop && (xpa=xpa->next) ) goto loop; + /* return the news */ + return(got); +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: XPAClientLoop + * + * Purpose: non-X programs event loop for handling XPA client events + * + * Returns: none + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +static int +XPAClientLoop (XPA xpa, int mode) +#else +static int XPAClientLoop(xpa, mode) + XPA xpa; + int mode; +#endif +{ + int got=0; + int sgot; + int doxpa=1; + int ltimeout; + char *s=NULL; + fd_set readfds; + fd_set writefds; + static int width=0; + struct timeval tv; + struct timeval *tvp; + + /* set width once */ + if( width == 0 ){ + width = FD_SETSIZE; + } + /* allow environment to turn off xpa server processing in client loop */ + if( (s=getenv("XPA_CLIENT_DOXPA")) && isfalse(s) ){ + doxpa = 0; + } + ltimeout = XPALongTimeout(); + FD_ZERO(&readfds); + FD_ZERO(&writefds); + while( XPAClientAddSelect(xpa, &readfds, &writefds) ){ + /* add other XPA's and process them as we process the client */ + if( (mode & XPA_CLIENT_SEL_XPA) && doxpa ){ + FPRINTF((stderr, "%sXPAClientLoop: will handle server reqs ...\n", _sp)); + XPAAddSelect(NULL, &readfds); + } + /* hopefully, a server will respond in a finite amount of time */ + if( ltimeout > 0 ){ + tv.tv_sec = ltimeout; + tv.tv_usec = 0; + tvp = &tv; + } + /* wait forever, if necessary */ + else{ + tvp = NULL; + } + /* add stdin to select, if there is one */ + if( xpa->ifd >= 0 ){ + xfd_set_stdin(xpa->ifd, &readfds); +#if HAVE_MINGW32 + /* BUT: for windows, we can't add stdin to select and therefore we + must set a short timeout and look manually */ + tv.tv_sec = 0; + tv.tv_usec = 10000; + /* this is the number of window iterations we will perform */ + if( ltimeout > 0 ) + ltimeout *= 100; + tvp = &tv; +#endif + } + /* wait for a server to respond */ + FPRINTF((stderr, "%sXPAClientLoop: waiting on select() ...\n", _sp)); + sgot = xselect(width, &readfds, &writefds, NULL, tvp); + FPRINTF((stderr, "%sXPAClientLoop: select returns: %d\n", _sp, sgot)); + /* error -- what should we do? */ + if( sgot < 0 ){ + if( xerrno == EINTR ){ + FD_ZERO(&readfds); + FD_ZERO(&writefds); + continue; + } + if( XPAVerbosity() ){ + perror("XPAClientLoop() select"); + } + exit(1); + } + /* timed out -- no one responded */ + else if( sgot == 0 ){ +#if HAVE_MINGW32 + if( xpa->ifd >= 0 ){ + if( ltimeout > 0 ){ + if( --ltimeout <= 0 ) + break; + } + } + else{ + break; + } +#else + break; +#endif + } + else{ + got += XPAClientProcessSelect(xpa, &readfds, &writefds, 0); + if( (mode & XPA_CLIENT_SEL_XPA) && doxpa ){ + got += XPAProcessSelect(&readfds, 0); + } + } + FD_ZERO(&readfds); + FD_ZERO(&writefds); + } + return(got); +} + +#if HAVE_MINGW32==0 +/* + *---------------------------------------------------------------------------- + * + * Routine: XPAClientLoopFork + * + * Purpose: non-X programs forked event loop for handling XPA client events + * + * Returns: number of clients "processed" + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +static int +XPAClientLoopFork (XPA xpa, int mode) +#else +static int XPAClientLoopFork(xpa, mode) + XPA xpa; + int mode; +#endif +{ + XPAClient client, tclient; + pid_t pid; + int got; + int fd[2]; + char active=1; +#ifndef USE_DOUBLE_FORK + struct sigaction act; + /* set up the signal handler to reap children (to avoid zombies) */ + act.sa_handler = sig_chld; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; +#ifdef SA_RESTART + act.sa_flags |= SA_RESTART; +#endif +#ifdef SA_NOCLDWAIT + act.sa_flags |= SA_NOCLDWAIT; +#endif + sigaction(SIGCHLD, &act, NULL); +#endif + + if( pipe(fd) < 0 ){ + got = 0; + } + else if( (pid = fork()) < 0 ){ + close(fd[0]); + close(fd[1]); + got=0; + } + else if( pid == 0 ){ /* child */ + /* child write to parent that he is active */ + close(fd[0]); + write(fd[1], &active, 1); + close(fd[1]); +#ifdef USE_DOUBLE_FORK + /* second fork prevents zombies: + when child/parent exits, second child is inherited + by init and thus is not a child of original parent */ + if( (pid = fork()) >= 0 ){ + /* child/parent exits */ + if( pid > 0 ) + exit(0); + /* new child goes on under init ... */ + } +#endif + /* enter the main loop and process */ + XPAIOCallsXPA(0); + XPAClientLoop(xpa, mode); + exit(0); + } else { /* parent */ + /* parent waits for child to wake up */ + close(fd[1]); + read(fd[0], &active, 1); + close(fd[0]); +#ifdef USE_DOUBLE_FORK + /* for double fork, also wait for intermediate process to exit */ + waitpid(pid, NULL, 0); +#endif + /* fake end of clients */ + for(got=0, client=xpa->clienthead; client!=NULL; ){ + got++; + tclient = client->next; + if( (client->status == XPA_CLIENT_PROCESSING) && (client->datafd >=0) ){ +#if HAVE_CYGWIN + /* In Cygwin, we call shutdown (as well as close) to avoid Windows + problems. The parent can't do this since the child is using the + sockets, so we just close the sockets explicitly here */ + xclose(client->datafd); + client->datafd = -1; + if( !xpa->persist ){ + xclose(client->cmdfd); + client->cmdfd = -1; + } +#endif + client->errptr = NULL; + /* remove this client if we are not meant to persist */ + if( !xpa->persist ){ + XPAClientFree(xpa, client); + } + /* otherwise mark as inactive */ + else{ + client->status = XPA_CLIENT_IDLE; + client->bytes = 0; + } + } + client = tclient; + } + } + return(got); +} +#endif + +/* + *---------------------------------------------------------------------------- + * + * Routine: XPAClientConnect + * + * Purpose: go to name service and get new names, merge with old, + * and connect to servers + * + * Returns: number of connections + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +static int +XPAClientConnect (XPA xpa, char *mode, char *xtemplate, int type) +#else +static int XPAClientConnect(xpa, mode, xtemplate, type) + XPA xpa; + char *mode; + char *xtemplate; + int type; +#endif +{ + int i; + int n; + int got; + int lp=0; + int total=0; + char **xclasses; + char **names; + char **methods; + char **infos; + char xtype[2]; + char xmode[SZ_LINE]; + char tbuf[SZ_LINE]; + char lbuf[SZ_LINE]; + XPAClient client; + + /* do some initialization */ + XPAInitEnv(); + + /* make sure we have a target */ + if( !xtemplate || !*xtemplate ) + return(0); + + /* make a string out of the type for the lookup */ + xtype[0] = type; + xtype[1] = '\0'; + + /* reset the number of clients we are processing */ + xpa->nclient = 0; + + /* look for specific proxy info */ + *xmode = '\0'; + if( mode ){ + strncpy(xmode, mode, SZ_LINE-1); + xmode[SZ_LINE-1] = '\0'; + } + if( keyword(xmode, "ns", lbuf, SZ_LINE) ){ + FPRINTF((stderr, "%sXPAClientConnect: using ns info: %s\n", _sp, lbuf)); + newdtable("(),"); + xclasses = (char **)xmalloc(sizeof(char *)); + names = (char **)xmalloc(sizeof(char *)); + methods = (char **)xmalloc(sizeof(char *)); + infos = (char **)xmalloc(sizeof(char *)); + if( word(lbuf, tbuf, &lp) ) + xclasses[0] = xstrdup(tbuf); + if( word(lbuf, tbuf, &lp) ) + names[0] = xstrdup(tbuf); + if( word(lbuf, tbuf, &lp) ) + methods[0] = xstrdup(tbuf); + infos[0] = xstrdup(XPA_DEF_CLIENT_INFO); + n = 1; + freedtable(); + } + /* else ask xpans for access points matching the template */ + else{ + n = XPANSLookup(xpa, + xtemplate, xtype, &xclasses, &names, &methods, &infos); + } + /* mark existing clients who do not match this template */ + for(got=0, client=xpa->clienthead; client !=NULL; client=client->next){ + for(i=0; i<n; i++){ + if( (client->type == type) && + (!strcmp(client->xclass, xclasses[i])) && + (!strcmp(client->name, names[i])) && + (!strcmp(client->method, methods[i])) && + (!strcmp(client->info, infos[i])) ){ + got++; + } + } + /* don't unmark if its a different type -- someone else might be active */ + if( !got && (client->type == type) ){ + client->status = XPA_CLIENT_IDLE; + } + } + /* add new clients for this type */ + for(i=0; i<n; i++){ + for(got=0, client=xpa->clienthead; client !=NULL; client=client->next){ + if( (client->type == type) && + (!strcmp(client->xclass, xclasses[i])) && + (!strcmp(client->name, names[i])) && + (!strcmp(client->method, methods[i])) && + (!strcmp(client->info, infos[i])) ){ + /* might have to change the template */ + if( strcmp(client->xtemplate, xtemplate) ){ + xfree(client->xtemplate); + client->xtemplate = xstrdup(xtemplate); + } + client->status = XPA_CLIENT_ACTIVE; + got++; + total++; + FPRINTF((stderr, "%sXPAClientConnect: existing match: %s %s %s\n", + _sp, xclasses[i], names[i], methods[i])); + break; + } + } + if( !got ){ + FPRINTF((stderr, "%sXPAClientConnect: calls XPAClientNew for %s:%s %s\n", + _sp, xclasses[i], names[i], methods[i])); + if( XPAClientNew(xpa, mode, xtemplate, type, + xclasses[i], names[i], methods[i], infos[i]) ) + total++; + } + /* done with these strings */ + xfree(xclasses[i]); + xfree(names[i]); + xfree(methods[i]); + xfree(infos[i]); + } + /* free up arrays alloc'ed by names server */ + if( n > 0 ){ + xfree(xclasses); + xfree(names); + xfree(methods); + xfree(infos); + } + return(total); +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: XPAClientStart + * + * Purpose: send init string to server and perform other authentication + * tasks + * + * Returns: 0 if success, -1 otherwise + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +static int +XPAClientStart (XPA xpa, XPAClient client, char *paramlist, char *mode) +#else +static int XPAClientStart(xpa, client, paramlist, mode) + XPA xpa; + XPAClient client; + char *paramlist; + char *mode; +#endif +{ + int fd=0; + int lp=0; + int flags; + int tries=0; + int dmode=0; + int keep_alive=1; + unsigned int ip=0; + unsigned short port; + char tbuf[SZ_LINE]; + char tbuf2[SZ_LINE]; + char lbuf[SZ_LINE]; + char *cmd=NULL; + char *method=NULL; + struct sockaddr_in sock_in; +#if HAVE_SYS_UN_H + struct sockaddr_un sock_un; +#endif + + switch(client->type){ + case 'a': + cmd = "xpaaccess"; + break; + case 'g': + cmd = "xpaget"; + break; + case 'i': + cmd = "xpainfo"; + break; + case 's': + cmd = "xpaset"; + break; + } + + /* get mode flags */ + XPAMode(mode, &(client->mode), "ack", XPA_CLIENT_ACK, 1); + if( client->type == 's' ) + XPAMode(mode, &(xpa->client_mode), "verify", XPA_CLIENT_VERIFY, 0); + /* package up and send the initialization message */ + strcpy(lbuf, cmd); + /* set and save the id value */ + snprintf(tbuf, SZ_LINE, "%c%d", client->type, id++); + if( client->id ) xfree(client->id); + client->id = xstrdup(tbuf); + /* if we are using the xpans proxy, we will want to call + accept() (server calls connect()) for the data channel */ + if( client->nsproxy ) + strcat(lbuf, " -a"); + /* set the value of the client big-endian-ness */ + snprintf(tbuf, SZ_LINE, " -e %s", XPAEndian() ? "big" : "little"); + strcat(lbuf, tbuf); + snprintf(tbuf, SZ_LINE, " -i %s", client->id); + strcat(lbuf, tbuf); + if( !(client->mode & XPA_CLIENT_ACK) ) + strcat(lbuf, " -n"); + if( strcmp(client->info, XPA_DEF_CLIENT_INFO) ){ + snprintf(tbuf, SZ_LINE, " -p %s", client->info); + strcat(lbuf, tbuf); + } + snprintf(tbuf, SZ_LINE, " %s:%s", client->xclass, client->name); + strcat(lbuf, tbuf); + if( paramlist && *paramlist ){ + strcat(lbuf, " "); + strncat(lbuf, paramlist, MAX(0,(int)(SZ_LINE-(int)strlen(lbuf)-2))); + } + strcat(lbuf, "\n"); + FPRINTF((stderr, "%sXPAClientStart: fd %d sends:\n%s", + _sp, client->cmdfd, lbuf)); + if( XPAPuts(NULL, client->cmdfd, lbuf, XPAShortTimeout()) <= 0 ){ + goto error; + } + + /* if xpainfo and no ack'ing, we are basically done */ + if( (client->type == 'i') && !(client->mode & XPA_CLIENT_ACK) ){ + goto done; + } + + /* authentication */ +retry: + lp = 0; + if( XPAGets(NULL, client->cmdfd, lbuf, SZ_LINE, XPAShortTimeout()) >0 ){ + FPRINTF((stderr, "%sXPAClientStart: fd %d received cmd:\n%s", _sp, + client->cmdfd, lbuf)); + /* this should never happen */ + if( !word(lbuf, tbuf, &lp) || (*tbuf == '?') ){ + snprintf(errbuf, SZ_LINE, + "XPA$WARNING: Protocol mismatch: id\n%s", lbuf); + goto error; + } + /* make sure we are dealing with a proper message */ + if( strcmp(tbuf, client->id) ){ + FPRINTF((stderr, "%sXPA$WARNING: ignoring out of sync message:\n", _sp)); + FPRINTF((stderr, "%s", lbuf)); + if( XPAVerbosity() > 1 ){ + fprintf(stderr, "XPA$WARNING: ignoring out of sync server message:\n"); + fprintf(stderr, "%s", lbuf); + } + goto retry; + } + + /* this should never happen */ + if( !word(lbuf, tbuf, &lp) ){ + snprintf(errbuf, SZ_LINE, "XPA$WARNING: missing BUF request\n%s", lbuf); + goto error; + } + if( !strcmp(tbuf, "XPA$NODATA") || !strcmp(tbuf, "XPA$NOBUF") ){ + xpa->nclient += 1; + goto started; + } + /* support 2.2 (DATA) and 2.0,2.1 (BUF) */ + else if( !strcmp(tbuf, "XPA$DATA") || !strcmp(tbuf, "XPA$BUF") ){ + if( !strcmp(tbuf, "XPA$DATA") ){ + dmode |= DATA_DATA; + if( !word(lbuf, tbuf, &lp) ){ + snprintf(errbuf, SZ_LINE, + "XPA$WARNING: missing DATA request type\n%s", lbuf); + goto error; + } + if( !strcmp(tbuf, "connect") ){ + method = client->method; + dmode |= DATA_CONNECT; + } + else if( !strcmp(tbuf, "accept") ){ + method = client->method; + dmode |= DATA_ACCEPT; + } + else{ + snprintf(errbuf, SZ_LINE, + "XPA$WARNING: invalid data connection request: %s (%s:%s)\n", + tbuf, client->xclass, client->name); + goto error; + } + } + else if( !strcmp(tbuf, "XPA$BUF") ){ + if( !word(lbuf, tbuf, &lp) ){ + snprintf(errbuf, SZ_LINE, + "XPA$ERROR: missing data buffer method (%s:%s%s)\n", + client->xclass, client->name, XPATimestamp()); + goto error; + } + method = tbuf; + dmode |= DATA_CONNECT; + } + /* handle connect-type requests */ + if( dmode & DATA_CONNECT ){ + switch(XPAMethod(method)){ + case XPA_INET: + XPAParseIpPort(method, &ip, &port); + /* connect to the server before we go further */ + if( (fd = xsocket(AF_INET, SOCK_STREAM, 0)) < 0 ){ + snprintf(errbuf, SZ_LINE, + "XPA$ERROR: bad socket for data chan (%s:%s%s)\n", + client->xclass, client->name, XPATimestamp()); + if( XPAVerbosity() ){ + perror("XPA client socket"); + } + goto error; + } + setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, + (char *)&keep_alive, sizeof(keep_alive)); + memset((char *)&sock_in, 0, sizeof(sock_in)); + sock_in.sin_family = AF_INET; + /* connect using the same ip we used for the command channel + (i.e., could be localhost) */ + sock_in.sin_addr.s_addr = htonl(client->ip); + sock_in.sin_port = htons(port); + FPRINTF((stderr, + "%sXPAClientStart: attempting dchan connect: %s\n", + _sp, method)); + if( connect(fd, (struct sockaddr *)&sock_in, sizeof(sock_in)) < 0 ){ + PERROR(("dchan connect")); + snprintf(errbuf, SZ_LINE, + "XPA$ERROR: can't connect to data chan (%s:%s%s)\n", + client->xclass, client->name, XPATimestamp()); + if( XPAVerbosity() ){ + perror("XPA client connect"); + } + xclose(fd); + goto error; + } + break; +#if HAVE_SYS_UN_H + case XPA_UNIX: +again: + /* open a socket and fill in socket information */ + if( (fd = xsocket(AF_UNIX, SOCK_STREAM, 0)) < 0 ){ + snprintf(errbuf, SZ_LINE, + "XPA$ERROR: bad socket for data chan (%s:%s%s)\n", + client->xclass, client->name, XPATimestamp()); + if( XPAVerbosity() ){ + perror("XPA client socket"); + } + goto error; + } + setsockopt(fd, 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); + FPRINTF((stderr, + "%sXPAClientStart: attempting dchan connect: %s\n", + _sp, method)); + if( connect(fd, (struct sockaddr *)&sock_un, sizeof(sock_un)) < 0 ){ + PERROR(("dchan connect")); + xclose(fd); + if( (xerrno == ECONNREFUSED) && (tries < XPA_RETRIES) ){ + tries++; + xclose(fd); + XPASleep(10); + goto again; + } + /* give up */ + else{ + snprintf(errbuf, SZ_LINE, + "XPA$ERROR: can't connect to data chan (%s:%s%s)\n", + client->xclass, client->name, XPATimestamp()); + if( XPAVerbosity() ){ + perror("XPA client connect"); + } + goto error; + } + } + break; +#endif + default: + snprintf(errbuf, SZ_LINE, + "XPA$ERROR: unknown connection method (%s:%s%s)\n", + client->xclass, client->name, XPATimestamp()); + goto error; + } + } + /* handle "doaccept" requests */ + else if( dmode & DATA_ACCEPT ){ + fd = XPAProxyAccept(xpa, XPANSMethod(NULL,2), + client->xclass, client->name, client->cmdfd, + NULL, NULL, tbuf2); + if( fd < 0 ){ + snprintf(errbuf, SZ_LINE, + "XPA$ERROR: can't connect to proxy server (%s:%s%s)\n", + client->xclass, client->name, XPATimestamp()); + goto error; + } + else if( *tbuf2 ){ + client->dataname = xstrdup(tbuf2); + } + } + + /* common code for either type of data connect request */ + client->datafd = fd; + /* make sure we close on exec */ + xfcntl(client->datafd, F_SETFD, FD_CLOEXEC); + /* for senders, set to no block mode */ + if( client->type == 's' ){ + /* save state and set in non-blocking mode */ + xfcntl_nonblock(client->datafd, flags); + } + xpa->nclient += 1; + goto started; + } + /* handle error message */ + else if( !strcmp(tbuf, "XPA$ERROR") ){ + snprintf(errbuf, SZ_LINE, "%s", &lbuf[lp]); + goto error; + } + /* everything else is an error */ + else{ + snprintf(errbuf, SZ_LINE, "%s", &lbuf[lp]); + goto error; + } + } + else{ + snprintf(errbuf, SZ_LINE, + "XPA$ERROR: no response from server during handshake (%s:%s%s)\n", + client->xclass, client->name, XPATimestamp()); + goto error; + } + +error: + FPRINTF((stderr, "Error in XPAClientStart: %s", errbuf)); + XPAClientFree(xpa, client); + return(-1); + +started: + /* it is necessary to add this this extra hack/ack between the + authentication ack and the error return (both from the server) + to avoid getting Nagle buffered. if we already did an accept, + however, we already sent a message and need not repeat it */ + if( !(dmode & DATA_ACCEPT) ){ + FPRINTF((stderr, "%sXPAClientStart: %d sending nagle\n", + _sp, client->cmdfd)); + XPAPuts(NULL, client->cmdfd, "xpanagle\n", XPAShortTimeout()); + } + + /* write a "data request" to the server on the data channel, supplying + the arguments to allow the server to find the associated command */ + if( dmode & DATA_DATA ){ + if( !word(lbuf, tbuf, &lp) ) + strcpy(tbuf, "?"); + if( !word(lbuf, tbuf2, &lp) ) + strcpy(tbuf2, "?"); + snprintf(lbuf, SZ_LINE, "xpadata -f %s %s\n", tbuf, tbuf2); + FPRINTF((stderr, + "%sXPAClientStart: sending data channel %d request: %s", _sp, + client->datafd, lbuf)); + if( XPAPuts(NULL, client->datafd, lbuf, XPAShortTimeout()) <0 ){ + snprintf(errbuf, SZ_LINE, + "XPA$ERROR: unable to issue data request: %s (%s:%s%s)\n", + lbuf, client->xclass, client->name, XPATimestamp()); + FPRINTF((stderr, "%sXPAClientStart: error returned is %s", _sp, errbuf)); + goto error; + } + } + +done: + /* mark as active and started */ + client->status = XPA_CLIENT_PROCESSING; + return(0); +} + +/* + *---------------------------------------------------------------------------- + * + * + * Public Routines + * + * + *---------------------------------------------------------------------------- + */ + +/* + *--------------------------------------------------------------------------- + * + * Routine: XPAClientValid + * + * Purpose: see if the xpa client struct is valid + * + * Results: 1 on success, 0 for failure + * + *--------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +int +XPAClientValid (XPA xpa) +#else +int XPAClientValid(xpa) + XPA xpa; +#endif +{ + return(_XPAValid(xpaclienthead, xpa, "c")); +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: XPAOpen + * + * Purpose: open a persistent XPA client connection + * + * Returns: XPA struct on success + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +XPA +XPAOpen (char *mode) +#else +XPA XPAOpen(mode) + char *mode; +#endif +{ + XPA xpa; + + /* allocate xpa struct */ + if( (xpa = (XPA)xcalloc(1, sizeof(XPARec))) == NULL ) + return(NULL); + /* add version */ + xpa->version = xstrdup(XPA_VERSION); + /* mark this as a client struct */ + xpa->type = xstrdup("c"); + /* mark as persistent so we don't destroy at the end of the transfer */ + xpa->persist = 1; + + /* add this xpa to end of list of client xpas */ + XPAListAdd(&xpaclienthead, xpa); + + return(xpa); +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: XPAClose + * + * Purpose: close a persistent XPA client connection + * + * Returns: none + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +void +XPAClose (XPA xpa) +#else +void XPAClose(xpa) + XPA xpa; +#endif +{ + XPAClient client, tclient; + NS ns, tns; + + /* ignore struct if its not a client struct */ + if( !XPAClientValid(xpa) ) + return; + + /* remove from list of client xpas */ + XPAListDel(&xpaclienthead, xpa); + + /* free each remaining client */ + for(client=xpa->clienthead; client!=NULL; ){ + tclient = client->next; + XPAClientFree(xpa, client); + client = tclient; + } + + /* 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 string space */ + if( xpa->version ) + xfree(xpa->version); + if( xpa->type ) + xfree(xpa->type); + if( xpa ) + xfree(xpa); +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: XPAGet + * + * Purpose: get XPA values + * + * Returns: 0 for success, -1 for failure + * len bytes of data returned in buf + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +int +XPAGet (XPA xpa, char *xtemplate, char *paramlist, char *mode, + char **bufs, size_t *lens, char **names, char **messages, int n) +#else +int XPAGet(xpa, xtemplate, paramlist, mode, bufs, lens, names, messages, n) + XPA xpa; + char *xtemplate; + char *paramlist; + char *mode; + char **bufs; + size_t *lens; + char **names; + char **messages; + int n; +#endif +{ + int i; + int oldmode=0; + int xmode=0; + int type='g'; + int idef=1; + int got=0; + char tbuf[SZ_LINE]; + XPAClient client, tclient; + + FPRINTF((stderr, "%sXPAGet: starting\n", _sp)); + /* if not persistent, we need a temp xpa struct; + (also ignore passed struct if its not a client struct) */ + if( (xpa == NULL) || strcmp(xpa->type, "c") ){ + if( (xpa = XPAOpen(NULL)) == NULL ) + return(-1); + /* mark this as not persistent */ + xpa->persist = 0; + } + /* save xpa mode -- this call might override */ + else{ + /* make sure we have a valid client handle */ + if( !XPAClientValid(xpa) ){ + if( XPAVerbosity() ){ + fprintf(stderr, "XPA$ERROR: invalid xpa client handle\n"); + } + return(-1); + } + oldmode = xpa->client_mode; + } + + /* these arrays are required */ + if( (bufs == NULL) || (lens == NULL) ){ + got = -1; + goto done; + } + + /* flag that we don't read from stdin */ + xpa->ifd = -1; + + /* zero out the return buffers */ + memset((char *)bufs, 0, ABS(n)*sizeof(char *)); + memset((char *)lens, 0, ABS(n)*sizeof(size_t)); + if( names != NULL ) + memset((char *)names, 0, ABS(n)*sizeof(char *)); + if( messages != NULL ) + memset((char *)messages, 0, ABS(n)*sizeof(char *)); + + /* connect to clients and grab data */ + if( XPAClientConnect(xpa, mode, xtemplate, type) >0 ){ + /* retrieve data from n active clients */ + for(client=xpa->clienthead; client!=NULL; ){ + tclient = client->next; + if( (client->type == type) && (client->status != XPA_CLIENT_IDLE) && + (got<ABS(n)) ){ + if( names != NULL ){ + snprintf(tbuf, SZ_LINE, "%s:%s %s", + client->xclass, client->name, client->method); + names[got] = xstrdup(tbuf); + } + if( XPAClientStart(xpa, client, paramlist, mode) >=0 ){ + /* we fill buffers */ + client->mode |= XPA_CLIENT_BUF; + client->bufptr = &(bufs[got]); + client->lenptr = &(lens[got]); + if( names != NULL ) + client->nameptr = &(names[got]); + if( messages != NULL ) + client->errptr = &(messages[got]); + } + else{ + if( messages != NULL ) + messages[got] = xstrdup(errbuf); + } + got++; + } + client = tclient; + } + /* if we have active clients */ + if( got ){ +#if HAVE_MINGW32==0 + /* check for loop modes */ + XPAMode(mode, &xmode, "dofork", XPA_CLIENT_SEL_FORK, 0); + /* dofork implies don't do xpa */ + if( xmode & XPA_CLIENT_SEL_FORK ) + idef = 0; + XPAMode(mode, &xmode, "doxpa", XPA_CLIENT_SEL_XPA, idef); + if( xmode & XPA_CLIENT_SEL_FORK ){ + XPAClientLoopFork(xpa, xmode); + } + else{ + /* enter the main loop and process */ + XPAClientLoop(xpa, xmode); + } +#else + XPAMode(mode, &xmode, "doxpa", XPA_CLIENT_SEL_XPA, idef); + XPAClientLoop(xpa, xmode); +#endif + } + } + +done: + /* look for clients who timed out */ + for(i=0, client=xpa->clienthead; client!=NULL; client=client->next){ + if( (client->type == type) && (client->status != XPA_CLIENT_IDLE) && + (i<ABS(n)) ){ + i++; + if( (client->status == XPA_CLIENT_PROCESSING) && ( messages != NULL) ){ + snprintf(errbuf, SZ_LINE, + "XPA$ERROR: no response from server callback (%s:%s%s)\n", + client->xclass, client->name, XPATimestamp()); + messages[i] = xstrdup(errbuf); + } + } + } + /* remove this xpa if we are not meant to persist */ + if( xpa && !xpa->persist ) + XPAClose(xpa); + /* restore xpa mode -- this call might override */ + else + xpa->client_mode = oldmode; + + /* return number of clients processes (including errors) */ + return(got); +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: XPAGetFd + * + * Purpose: get XPA values + * + * Returns: 0 for success, -1 for failure + * len bytes of data returned in buf + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +int +XPAGetFd (XPA xpa, char *xtemplate, char *paramlist, char *mode, + int *fds, char **names, char **messages, int n) +#else +int XPAGetFd(xpa, xtemplate, paramlist, mode, fds, names, messages, n) + XPA xpa; + char *xtemplate; + char *paramlist; + int *fds; + char *mode; + char **names; + char **messages; + int n; +#endif +{ + int i; + int oldmode=0; + int xmode=0; + int got=0; + int type='g'; + int idef=1; + char tbuf[SZ_LINE]; + XPAClient client, tclient; + + FPRINTF((stderr, "%sXPAGetFd: starting\n", _sp)); + /* if not persistent, we need a temp xpa struct; + (also ignore passed struct if its not a client struct) */ + if( (xpa == NULL) || strcmp(xpa->type, "c") ){ + if( (xpa = XPAOpen(NULL)) == NULL ) + return(-1); + /* mark this as not persistent */ + xpa->persist = 0; + } + /* save xpa mode -- this call might override */ + else{ + /* make sure we have a valid client handle */ + if( !XPAClientValid(xpa) ){ + if( XPAVerbosity() ){ + fprintf(stderr, "XPA$ERROR: invalid xpa client handle\n"); + } + return(-1); + } + oldmode = xpa->client_mode; + } + + /* flag that we don't read from stdin */ + xpa->ifd = -1; + + /* zero out the return buffers */ + if( names != NULL ) + memset((char *)names, 0, ABS(n)*sizeof(char *)); + if( messages != NULL ) + memset((char *)messages, 0, ABS(n)*sizeof(char *)); + + /* connect to clients and grab data */ + if( XPAClientConnect(xpa, mode, xtemplate, type) > 0 ){ + /* retrieve data from n active clients */ + for(client=xpa->clienthead; client!=NULL; ){ + tclient = client->next; + if( (client->type == type) && (client->status != XPA_CLIENT_IDLE) && + (got<ABS(n)) ){ + if( names != NULL ){ + snprintf(tbuf, SZ_LINE, "%s:%s %s", + client->xclass, client->name, client->method); + names[got] = xstrdup(tbuf); + } + if( XPAClientStart(xpa, client, paramlist, mode) >= 0 ){ + /* we write to an fd */ + client->mode |= XPA_CLIENT_FD; + /* negative value => one channel for all clients */ + if( n < 0 ) + client->fd = fds[0]; + else + client->fd = fds[got]; + client->bufptr = (char **)xcalloc(1, sizeof(char *)); + client->lenptr = (size_t *)xcalloc(1, sizeof(size_t)); + if( names != NULL ) + client->nameptr = &(names[got]); + if( messages != NULL ) + client->errptr = &(messages[got]); + } + else{ + if( messages != NULL ) + messages[got] = xstrdup(errbuf); + } + got++; + } + client = tclient; + } + /* if we have active clients */ + if( got ){ +#if HAVE_MINGW32==0 + /* check for loop modes */ + XPAMode(mode, &xmode, "dofork", XPA_CLIENT_SEL_FORK, 0); + /* dofork implies don't do xpa */ + if( xmode & XPA_CLIENT_SEL_FORK ) + idef = 0; + XPAMode(mode, &xmode, "doxpa", XPA_CLIENT_SEL_XPA, idef); + if( xmode & XPA_CLIENT_SEL_FORK ){ + XPAClientLoopFork(xpa, xmode); + } + else{ + /* enter the main loop and process */ + XPAClientLoop(xpa, xmode); + } +#else + XPAMode(mode, &xmode, "doxpa", XPA_CLIENT_SEL_XPA, idef); + XPAClientLoop(xpa, xmode); +#endif + } + } + /* look for clients who timed out */ + for(i=0, client=xpa->clienthead; client!=NULL; client=client->next){ + if( (client->type == type) && (client->status != XPA_CLIENT_IDLE) && + (i<ABS(n)) ){ + i++; + if( (client->status == XPA_CLIENT_PROCESSING) && ( messages != NULL) ){ + snprintf(errbuf, SZ_LINE, + "XPA$ERROR: no response from server callback (%s:%s%s)\n", + client->xclass, client->name, XPATimestamp()); + messages[i] = xstrdup(errbuf); + } + } + } + /* remove this xpa if we are not meant to persist */ + if( xpa && !xpa->persist ) + XPAClose(xpa); + /* restore xpa mode -- this call might override */ + else + xpa->client_mode = oldmode; + + /* return number of clients processes (including errors) */ + return(got); +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: XPASet + * + * Purpose: set XPA values + * + * Returns: 0 for success, -1 for failure + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +int +XPASet (XPA xpa, char *xtemplate, char *paramlist, char *mode, + char *buf, size_t len, char **names, char **messages, int n) +#else +int XPASet(xpa, xtemplate, paramlist, mode, buf, len, names, messages, n) + XPA xpa; + char *xtemplate; + char *paramlist; + char *mode; + char *buf; + size_t len; + char **names; + char **messages; + int n; +#endif +{ + int i; + int oldmode=0; + int xmode=0; + int got=0; + int type='s'; + int idef=1; + char tbuf[SZ_LINE]; + XPAClient client, tclient; + + FPRINTF((stderr, "%sXPASet: starting\n", _sp)); + /* if not persistent, we need a temp xpa struct; + (also ignore passed struct if its not a client struct) */ + if( (xpa == NULL) || strcmp(xpa->type, "c") ){ + if( (xpa = XPAOpen(NULL)) == NULL ) + return(-1); + /* mark this as not persistent */ + xpa->persist = 0; + } + /* save xpa mode -- this call might override */ + else{ + /* make sure we have a valid client handle */ + if( !XPAClientValid(xpa) ){ + if( XPAVerbosity() ){ + fprintf(stderr, "XPA$ERROR: invalid xpa client handle\n"); + } + return(-1); + } + oldmode = xpa->client_mode; + } + + /* flag that we don't read from stdin */ + xpa->ifd = -1; + + /* zero out the return buffers */ + if( names != NULL ) + memset((char *)names, 0, ABS(n)*sizeof(char *)); + if( messages != NULL ) + memset((char *)messages, 0, ABS(n)*sizeof(char *)); + + /* connect to clients and grab data */ + if( XPAClientConnect(xpa, mode, xtemplate, type) >0 ){ + /* retrieve data from n active clients */ + for(client=xpa->clienthead; client!=NULL; ){ + tclient = client->next; + if( (client->type == type) && (client->status != XPA_CLIENT_IDLE) && + (got<ABS(n)) ){ + if( names != NULL ){ + snprintf(tbuf, SZ_LINE, "%s:%s %s", + client->xclass, client->name, client->method); + names[got] = xstrdup(tbuf); + } + if( XPAClientStart(xpa, client, paramlist, mode) >= 0 ){ + /* we fill buffers */ + client->mode |= XPA_CLIENT_BUF; + client->buf = buf; + client->len = len; + if( names != NULL ) + client->nameptr = &(names[got]); + if( messages != NULL ) + client->errptr = &(messages[got]); + } + else{ + if( messages != NULL ) + messages[got] = xstrdup(errbuf); + } + got++; + } + client = tclient; + } + /* if we have active clients */ + if( got ){ +#if HAVE_MINGW32==0 + /* check for loop modes */ + XPAMode(mode, &xmode, "dofork", XPA_CLIENT_SEL_FORK, 0); + /* dofork implies don't do xpa */ + if( xmode & XPA_CLIENT_SEL_FORK ) + idef = 0; + XPAMode(mode, &xmode, "doxpa", XPA_CLIENT_SEL_XPA, idef); + if( xmode & XPA_CLIENT_SEL_FORK ){ + XPAClientLoopFork(xpa, xmode); + } + else{ + /* enter the main loop and process */ + XPAClientLoop(xpa, xmode); + } +#else + XPAMode(mode, &xmode, "doxpa", XPA_CLIENT_SEL_XPA, idef); + XPAClientLoop(xpa, xmode); +#endif + } + } + /* look for clients who timed out */ + for(i=0, client=xpa->clienthead; client!=NULL; client=client->next){ + if( (client->type == type) && (client->status != XPA_CLIENT_IDLE) && + (i<ABS(n)) ){ + i++; + if( (client->status == XPA_CLIENT_PROCESSING) && ( messages != NULL) ){ + snprintf(errbuf, SZ_LINE, + "XPA$ERROR: no response from server callback (%s:%s%s)\n", + client->xclass, client->name, XPATimestamp()); + messages[i] = xstrdup(errbuf); + } + } + } + /* remove this xpa if we are not meant to persist */ + if( xpa && !xpa->persist ) + XPAClose(xpa); + /* restore xpa mode -- this call might override */ + else + xpa->client_mode = oldmode; + + /* return number of clients processes (including errors) */ + return(got); +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: XPASetFd + * + * Purpose: set XPA values + * + * Returns: 0 for success, -1 for failure + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +int +XPASetFd (XPA xpa, char *xtemplate, char *paramlist, char *mode, + int fd, char **names, char **messages, int n) +#else +int XPASetFd(xpa, xtemplate, paramlist, mode, fd, names, messages, n) + XPA xpa; + char *xtemplate; + char *paramlist; + char *mode; + int fd; + char **names; + char **messages; + int n; +#endif +{ + int i; + int oldmode=0; + int xmode=0; + int got=0; + int got2=0; + int type='s'; + int idef=1; + int flags; + char *s; + char tbuf[SZ_LINE]; + XPAClient client, tclient; + + FPRINTF((stderr, "%sXPASetFd: starting\n", _sp)); + /* if not persistent, we need a temp xpa struct; + (also ignore passed struct if its not a client struct) */ + if( (xpa == NULL) || strcmp(xpa->type, "c") ){ + if( (xpa = XPAOpen(NULL)) == NULL ) + return(-1); + /* mark this as not persistent */ + xpa->persist = 0; + } + /* save xpa mode -- this call might override */ + else{ + /* make sure we have a valid client handle */ + if( !XPAClientValid(xpa) ){ + if( XPAVerbosity() ){ + fprintf(stderr, "XPA$ERROR: invalid xpa client handle\n"); + } + return(-1); + } + oldmode = xpa->client_mode; + } + + /* Set non-blocking mode for the input fd, if its not a tty */ + xpa->ifd = fd; + if( isatty(xpa->ifd) == 0 ){ + /* save state and set in non-blocking mode */ + xfcntl_nonblock(xpa->ifd, flags); + } + /* zero out the return buffers */ + if( names != NULL ) + memset((char *)names, 0, ABS(n)*sizeof(char *)); + if( messages != NULL ) + memset((char *)messages, 0, ABS(n)*sizeof(char *)); + + /* connect to clients and grab data */ + if( XPAClientConnect(xpa, mode, xtemplate, type) >0 ){ + /* open clients all at once */ + for(client=xpa->clienthead; client!=NULL; ){ + tclient = client->next; + if( (client->type == type) && (client->status != XPA_CLIENT_IDLE) && + (got<ABS(n)) ){ + if( names != NULL ){ + snprintf(tbuf, SZ_LINE, "%s:%s %s", + client->xclass, client->name, client->method); + names[got] = xstrdup(tbuf); + } + if( XPAClientStart(xpa, client, paramlist, mode) >= 0 ){ + /* we fill buffers */ + client->mode |= XPA_CLIENT_FD; + if( names != NULL ) + client->nameptr = &(names[got]); + if( messages != NULL ) + client->errptr = &(messages[got]); + } + else{ + if( messages != NULL ) + messages[got] = xstrdup(errbuf); + } + got++; + } + client = tclient; + } + /* if we have active clients */ + if( got ){ + /* if fd is null, user did not want to send data, just the paramlist */ + if( fd < 0 ){ + for(client=xpa->clienthead; client!=NULL; ){ + tclient = client->next; + if( (client->type == type) && (client->status != XPA_CLIENT_IDLE) && + (got<ABS(n)) ){ + XPAClientDataSent(xpa, client); + s = XPAClientEnd(xpa, client); + if( (messages != NULL) && (messages[got2] == NULL) ) + messages[got2] = xstrdup(s); + got2++; + } + client = tclient; + } + } + else{ +#if HAVE_MINGW32==0 + /* check for loop modes */ + XPAMode(mode, &xmode, "dofork", XPA_CLIENT_SEL_FORK, 0); + /* dofork implies don't do xpa */ + if( xmode & XPA_CLIENT_SEL_FORK ) + idef = 0; + XPAMode(mode, &xmode, "doxpa", XPA_CLIENT_SEL_XPA, idef); + if( xmode & XPA_CLIENT_SEL_FORK ){ + XPAClientLoopFork(xpa, xmode); + } + else{ + /* enter the main loop and process */ + XPAClientLoop(xpa, xmode); + } +#else + XPAMode(mode, &xmode, "doxpa", XPA_CLIENT_SEL_XPA, idef); + XPAClientLoop(xpa, xmode); +#endif + } + } + } + /* look for clients who timed out */ + for(i=0, client=xpa->clienthead; client!=NULL; client=client->next){ + if( (client->type == type) && (client->status != XPA_CLIENT_IDLE) && + (i<ABS(n)) ){ + i++; + if( (client->status == XPA_CLIENT_PROCESSING) && ( messages != NULL) ){ + snprintf(errbuf, SZ_LINE, + "XPA$ERROR: no response from server callback (%s:%s%s)\n", + client->xclass, client->name, XPATimestamp()); + messages[i] = xstrdup(errbuf); + } + } + } + /* reset flags, if necessary */ + if( xpa->ifd >=0 && (isatty(xpa->ifd) ==0) ){ + xfcntl(xpa->ifd, F_SETFL, flags); + } + /* free all input structs */ + XPAClientFreeAllInputs(xpa); + /* remove this xpa if we are not meant to persist */ + if( xpa && !xpa->persist ) + XPAClose(xpa); + /* restore xpa mode -- this call might override */ + else + xpa->client_mode = oldmode; + + /* return number of clients processes (including errors) */ + return(got); +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: XPAInfo + * + * Purpose: send XPA info + * + * Returns: 0 for success, -1 for failure + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +int +XPAInfo (XPA xpa, char *xtemplate, char *paramlist, char *mode, + char **names, char **messages, int n) +#else +int XPAInfo(xpa, xtemplate, paramlist, mode, names, messages, n) + XPA xpa; + char *xtemplate; + char *paramlist; + char *mode; + char **names; + char **messages; + int n; +#endif +{ + int i; + int oldmode=0; + int got=0; + char type='i'; + char *s; + char tbuf[SZ_LINE]; + XPAClient client, tclient; + + /* if not persistent, we need a temp xpa struct; + (also ignore passed struct if its not a client struct) */ + if( (xpa == NULL) || strcmp(xpa->type, "c") ){ + if( (xpa = XPAOpen(NULL)) == NULL ) + return(-1); + /* mark this as not persistent */ + xpa->persist = 0; + } + /* save xpa mode -- this call might override */ + else{ + /* make sure we have a valid client handle */ + if( !XPAClientValid(xpa) ){ + if( XPAVerbosity() ){ + fprintf(stderr, "XPA$ERROR: invalid xpa client handle\n"); + } + return(-1); + } + oldmode = xpa->client_mode; + } + + /* flag that we don't read from stdin */ + xpa->ifd = -1; + + /* zero out the return buffers */ + if( names != NULL ) + memset((char *)names, 0, ABS(n)*sizeof(char *)); + if( messages != NULL ) + memset((char *)messages, 0, ABS(n)*sizeof(char *)); + + /* connect to clients and grab data */ + if( XPAClientConnect(xpa, mode, xtemplate, type) >0 ){ + /* retrieve data from n active clients */ + for(client=xpa->clienthead; client!=NULL; ){ + tclient = client->next; + if( (client->type == type) && (client->status != XPA_CLIENT_IDLE) && + (got<ABS(n)) ){ + if( names != NULL ){ + snprintf(tbuf, SZ_LINE, "%s:%s %s", + client->xclass, client->name, client->method); + names[got] = xstrdup(tbuf); + } + if( XPAClientStart(xpa, client, paramlist, mode) >= 0 ){ + XPAClientDataSent(xpa, client); + s = XPAClientEnd(xpa, client); + if( (messages != NULL) && (messages[got] == NULL) ) + messages[got] = xstrdup(s); + } + else{ + if( (messages != NULL) && (messages[got] == NULL) ) + messages[got] = xstrdup(errbuf); + } + got++; + } + client = tclient; + } + } + /* look for clients who timed out */ + for(i=0, client=xpa->clienthead; client!=NULL; client=client->next){ + if( (client->type == type) && (client->status != XPA_CLIENT_IDLE) && + (i<ABS(n)) ){ + i++; + if( (client->status == XPA_CLIENT_PROCESSING) && ( messages != NULL) ){ + snprintf(errbuf, SZ_LINE, + "XPA$ERROR: no response from server callback (%s:%s%s)\n", + client->xclass, client->name, XPATimestamp()); + messages[i] = xstrdup(errbuf); + } + } + } + /* remove this xpa if we are not meant to persist */ + if( xpa && !xpa->persist ) + XPAClose(xpa); + /* restore xpa mode -- this call might override */ + else + xpa->client_mode = oldmode; + + /* return number of clients processes (including errors) */ + return(got); +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: XPAAccess + * + * Purpose: determine if XPA access point is available + * + * Returns: 0 for success, -1 for failure + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +int +XPAAccess (XPA xpa, char *xtemplate, char *paramlist, char *mode, + char **names, char **messages, int n) +#else +int XPAAccess(xpa, xtemplate, paramlist, mode, names, messages, n) + XPA xpa; + char *xtemplate; + char *paramlist; + char *mode; + char **names; + char **messages; + int n; +#endif +{ + int i; + int oldmode=0; + int xmode=0; + int got=0; + int type='a'; + char *s; + char *ind1, *ind2; + char tbuf[SZ_LINE]; + XPAClient client, tclient; + + /* if not persistent, we need a temp xpa struct; + (also ignore passed struct if its not a client struct) */ + if( (xpa == NULL) || strcmp(xpa->type, "c") ){ + if( (xpa = XPAOpen(NULL)) == NULL ) + return(-1); + /* mark this as not persistent */ + xpa->persist = 0; + } + /* save xpa mode -- this call might override */ + else{ + /* make sure we have a valid client handle */ + if( !XPAClientValid(xpa) ){ + if( XPAVerbosity() ){ + fprintf(stderr, "XPA$ERROR: invalid xpa client handle\n"); + } + return(-1); + } + oldmode = xpa->client_mode; + } + + /* flag that we don't read from stdin */ + xpa->ifd = -1; + + /* zero out the return buffers */ + if( names != NULL ) + memset((char *)names, 0, ABS(n)*sizeof(char *)); + if( messages != NULL ) + memset((char *)messages, 0, ABS(n)*sizeof(char *)); + + /* connect to clients and grab data */ + if( XPAClientConnect(xpa, mode, xtemplate, type) >0 ){ + /* retrieve data from n active clients */ + for(client=xpa->clienthead; client!=NULL; ){ + tclient = client->next; + if( (client->type == type) && (client->status != XPA_CLIENT_IDLE) && + (got<ABS(n)) ){ + if( names != NULL ){ + snprintf(tbuf, SZ_LINE, "%s:%s %s", + client->xclass, client->name, client->method); + names[got] = xstrdup(tbuf); + } + if( XPAClientStart(xpa, client, paramlist, mode) >=0 ){ + XPAClientDataSent(xpa, client); + s = XPAClientEnd(xpa, client); + if( (messages != NULL) && (messages[got] == NULL) ) + messages[got] = xstrdup(s); + } + else{ + if( (messages != NULL) && (messages[got] == NULL) ) + messages[got] = xstrdup(errbuf); + } + /* might have to fix the name if was an explicit mach:port */ + if( names && names[got] && *errbuf && + !strncmp(names[got], "?:?", 3) && + (ind1=strrchr(errbuf, '(')) && (ind2=strrchr(errbuf, ')')) ){ + ind1++; + strncpy(tbuf, ind1, ind2-ind1); + tbuf[ind2-ind1] = '\0'; + xfree(names[got]); + names[got] = xstrdup(tbuf); + } + got++; + } + client = tclient; + } + /* if we have active clients */ + if( got ){ + /* check for loop modes */ + XPAMode(mode, &xmode, "doxpa", XPA_CLIENT_SEL_XPA, 1); + /* enter the main loop and process */ + XPAClientLoop(xpa, xmode); + } + } + /* look for clients who timed out */ + for(i=0, client=xpa->clienthead; client!=NULL; client=client->next){ + if( (client->type == type) && (client->status != XPA_CLIENT_IDLE) && + (i<ABS(n)) ){ + i++; + if( (client->status == XPA_CLIENT_PROCESSING) && ( messages != NULL) ){ + snprintf(errbuf, SZ_LINE, + "XPA$ERROR: no response from server callback (%s:%s%s)\n", + client->xclass, client->name, XPATimestamp()); + messages[i] = xstrdup(errbuf); + } + } + } + /* remove this xpa if we are not meant to persist */ + if( xpa && !xpa->persist ) + XPAClose(xpa); + /* restore xpa mode -- this call might override */ + else + xpa->client_mode = oldmode; + + /* return number of clients processes (including errors) */ + return(got); +} + |