/***********************************************************************/ /* Open Visualization Data Explorer */ /* (C) Copyright IBM Corp. 1989,1999 */ /* ALL RIGHTS RESERVED */ /* This code licensed under the */ /* "IBM PUBLIC LICENSE - Open Visualization Data Explorer" */ /***********************************************************************/ #include #include "../base/UIConfig.h" #include #include #include #if !defined(OS2) && !defined(DXD_WIN) #include #endif #ifndef DXD_HAS_WINSOCKETS #include #endif #ifdef DXD_WIN #include #else #include #endif #ifndef DXD_HAS_WINSOCKETS #include #endif #include #include #include #if defined(ibm6000) || defined(pgcc) || defined(__METAWARE_HC) #include #endif #if defined(OS2) || defined(DXD_HAS_WINSOCKETS) #include #define write(a,b,c) _dxl_os2_send(a,b,c,0) #define read(a,b,c) _dxl_os2_recv(a,b,c,0) #endif #include "../base/defines.h" #include "dxlP.h" typedef int (*DXLPacketPredicate)(DXLConnection *, DXLEvent *, void *); static int _dxl_ReadFromSocket(DXLConnection *conn); static int _dxl_WaitForReadable(DXLConnection *conn); static int _dxl_IsReadable(DXLConnection *conn); static void PrintEvent(DXLEvent *e); static void PrintEventList(DXLEvent *e); static DXLError DXLGetPacket(DXLConnection *, DXLPacketTypeEnum, int, DXLEvent *, int, DXLPacketPredicate, const void *); #ifdef MAX # undef MAX #endif #define MAX(x,y) ((x) > (y)? (x): (y)) #define STRLEN(A) (A ? strlen(A) : 0) char *_DXLPacketTypes[] = { NULL, "$int", /* PACK_INTERRUPT */ "$sys", /* PACK_SYSTEM */ "$ack", /* PACK_ACK */ "$mac", /* PACK_MACRODEF */ "$for", /* PACK_FOREGROUND */ "$bac", /* PACK_BACKGROUND */ "$err", /* PACK_ERROR */ "$mes", /* PACK_MESSAGE */ "$inf", /* PACK_INFO */ "$lin", /* PACK_LINQUIRY */ "$lre", /* PACK_LRESPONSE */ "$dat", /* PACK_LDATA */ "$sin", /* PACK_SINQUIRY */ "$sre", /* PACK_SRESPONSE */ "$dat", /* PACK_SDATA */ "$vin", /* PACK_VINQUIRY */ "$vre", /* PACK_VRESPONSE */ "$dat", /* PACK_VDATA */ "$com", /* PACK_COMPLETE */ "$imp", /* PACK_IMPORT */ "$imi", /* PACK_IMPORTINFO */ "$lnk" /* PACK_LINK */ }; int _DXLNumPackets = (sizeof(_DXLPacketTypes)/sizeof(_DXLPacketTypes[0])); DXLEvent* DXLNewEvent(int serialId, DXLPacketTypeEnum ptype, const char *msg) { int msgsize = 0; DXLEvent *event = (DXLEvent*)malloc(sizeof(DXLEvent)); if (!event) return NULL; event->serial = serialId; event->type = ptype; if (msg) { msgsize = strlen(msg) + 1; if (msgsize > DXL_EVENT_BUF_SIZE) event->contents = malloc(msgsize*sizeof(char)); else event->contents = event->buffer; strcpy(event->contents,msg); } else { event->contents = event->buffer; event->buffer[0] = '\0'; } event->contentsSize = msgsize; event->next = NULL; return event; } DXLError DXLClearEvent(DXLEvent *event) { if (event->contents && event->contents != event->buffer) free(event->contents); event->next = NULL; event->contentsSize = 0; event->buffer[0] = '\0'; event->contents = event->buffer; return OK; } DXLError DXLFreeEvent(DXLEvent *event) { if (DXLClearEvent(event)) free(event); return OK; } DXLError DXLCopyEvent(DXLEvent *dst, DXLEvent *src) { if (src->contentsSize > DXL_EVENT_BUF_SIZE) dst->contents = malloc(src->contentsSize); else dst->contents = dst->buffer; dst->contentsSize = src->contentsSize; memcpy(dst->contents, src->contents, src->contentsSize); return OK; } DXLError DXLSend(DXLConnection *conn, const char *string) { DXLPacketTypeEnum ptype; int sts; if (conn->dxuiConnected) { if (conn->majorVersion <= 2) ptype = PACK_FOREGROUND; else ptype = PACK_LINK; } else if (conn->macroDef) { DXLSendUnpacketized(conn, string); } else { /* FIXME: is it sufficient to always send in the foreground */ ptype = PACK_FOREGROUND; } return DXLSendPacket(conn, ptype, string) >= 0 ? OK : ERROR; } #ifdef DEBUG_MESSAGES static int first = 1; static FILE *dbg = NULL; #endif int /* packet id that was sent, -1 on error */ DXLSendPacket(DXLConnection *conn, DXLPacketTypeEnum ptype, const char *string) { int l = STRLEN(string); char *buffer = malloc(l + 50); int packetId, sts = 0; int written = 0; if (!conn || conn->fd < 0 || (ptype > _DXLNumPackets) || (ptype <= 0)) return ERROR; packetId = conn->id++; if (ptype == PACK_MACRODEF) l = SPRINTF(buffer,"|%d|%s|0|\n", packetId, _DXLPacketTypes[ptype]); else if (ptype == PACK_INTERRUPT) { l = SPRINTF(buffer,"|%d|%s|1|0|\n", packetId, _DXLPacketTypes[ptype]); } else if (l == 0) l = SPRINTF(buffer,"|%d|%s|0||\n", packetId, _DXLPacketTypes[ptype]); else l = SPRINTF(buffer, "|%d|%s|%d|%s|\n", packetId, _DXLPacketTypes[ptype], l, string); #ifdef DEBUG_MESSAGES if (first) { first = 0; dbg = fopen("debug", "w"); } else dbg = fopen("debug", "a"); fprintf(dbg, "--> %s", buffer); fclose(dbg); #endif do { if (_dxl_ReadFromSocket(conn) < 0) { sts = -1; break; } if ((sts == 0) && conn->debugMessaging) fprintf(stderr, "Sending -> %s\n",buffer); sts = write(conn->fd, buffer + written, l); l -= sts; written += sts; } while (sts > 0 && l > 0); free(buffer); if (sts < 0) { _dxl_InvalidateSocket(conn); packetId = -1; goto done; } else if (ptype != PACK_MACRODEF && conn->synchronous) { if (!DXLSync(conn)) { packetId = -1; goto done; } } done: return packetId; } int DXLQuery(DXLConnection *conn, const char *string, const int length, char *result) { int l = STRLEN(string); int found, sts = 0; int requestId, written = 0; DXLEvent event; DXLPacketTypeEnum ptype; if (conn->fd < 0) goto error; if (conn->dxuiConnected) { if (conn->majorVersion <= 2) ptype = PACK_FOREGROUND; else ptype = PACK_LINK; } else { /* * FIXME: This probably is not the correct code, * but it serves as a marker. */ ptype = PACK_FOREGROUND; fprintf(stderr,"DXLQuery() not implemented for dexec connections\n"); goto error; } requestId = DXLSendPacket(conn,ptype,string); if (requestId < 0) goto error; found = 0; if (conn->dxuiConnected) { if (DXLGetPacketId(conn, PACK_LRESPONSE, requestId, &event)) found = 1; } else { /* include "filename" causes completes to come back that match * the zero'th sync */ if (DXLGetPacketId(conn, PACK_COMPLETE, 0, &event)) found = 1; } if (!found) { _dxl_InvalidateSocket(conn); DXLClearEvent(&event); goto error; } strncpy(result, event.contents, length); DXLClearEvent(&event); return length; error: return -1; } static void RemoveEvent(DXLConnection *conn, DXLEvent *e) { DXLEvent *event, *last_event = NULL; for (event = conn->events; event ; event = event->next) { if (event == e) break; last_event = event; } if (event == e) { if (e == conn->events) { /* * First item on list */ conn->events = e->next; if (e == conn->lastEvent) /* ...and last item */ conn->lastEvent = NULL; } else if (e == conn->lastEvent) { /* * Last item on list */ conn->lastEvent = last_event; last_event->next = NULL; } else { /* * Middle item on list */ last_event->next = e->next; } } conn->nEvents--; } static void AppendEvent(DXLConnection *conn, DXLEvent *e) { if (conn->events == NULL) { conn->events = e; } else { conn->lastEvent->next = e; } conn->lastEvent = e; conn->nEvents++; e->next = NULL; /* Just to be sure */ /* * We've pulled an event off the socket and onto the local queue. * If the event handler is dependent on a select() on the socket to * determine that there's something to do, it won't find anything. * So we install an idle-time event processor that will check for * something to do when the system is otherwise idle. */ _dxf_InstallIdleEventHandler(conn); } DXLError DXLNextPacket(DXLConnection *conn, DXLEvent *event) { while(conn->nEvents == 0) { _dxl_WaitForReadable(conn); if (_dxl_ReadFromSocket(conn) == 0) return ERROR; } if (conn->events->type == PACK_ERROR) _DXLError(conn, conn->events->contents); DXLCopyEvent(event, conn->events); RemoveEvent(conn, conn->events); DXLFreeEvent(conn->events); return OK; } DXLWaitForEvent(DXLConnection *conn) { if(conn->nEvents == 0) _dxl_WaitForReadable(conn); return OK; } static void DXLDispatchEvent(DXLConnection *conn, DXLEvent *e) { RemoveEvent(conn,e); if (conn->debugMessaging) { fprintf(stderr, "Dispatching <- "); PrintEvent(e); } _dxl_HandleMessage(conn, e->type, e->serial, e->contents); } DXLError DXLProcessEventList(DXLConnection *conn) { int i; DXLEvent *e; while (_dxl_IsReadable(conn)) if (_dxl_ReadFromSocket(conn) == 0) return ERROR; while (NULL != (e = conn->events)) { DXLDispatchEvent(conn,e); DXLFreeEvent(e); } conn->nEvents = 0; /* * Now we have cleared all the input from both the socket and the * input queue in conn, so we don't need an idle-time input check. */ _dxf_ClearIdleEventHandler(conn); return OK; error: return ERROR; } /* * Starting with the given event, search the event list for the event * matching the given type and requestId. If requestId is < 0 then we * don't try and match the requestId. Further, if pp is not NULL then * we also try and match the event with a call to (pp)(conn,event,arg). * */ static DXLEvent* DXLFindEventInList(DXLConnection *conn, DXLEvent *event, DXLPacketTypeEnum type, int requestId, DXLPacketPredicate pp, const void *arg) { DXLEvent *e; for (e=event ; e ; ) { if ((e->type == type) && ((requestId < 0) || (e->serial == requestId)) && (!pp || (pp)(conn,e,(char *)arg))) break; e = e->next; } return e; } /* * Get the packet specified by the given packet type and the given id. * If id is <= 0, then we don't try and match the ids with packets. * If pp is not NULL, then we call it on the event (pp(conn,e,arg)), * to see if it is a match. * If we find an event with a match, given the above criteria, then * we copy the event into the user structure . */ static int DXLCheckPacket(DXLConnection *conn, DXLPacketTypeEnum type, int requestId, DXLEvent *event, DXLPacketPredicate pp, const void *arg) { DXLEvent *e; e = DXLFindEventInList(conn,conn->events,type,requestId, pp, arg); if (e) { DXLCopyEvent(event, e); return 1; } return 0; } /* * Get the packet specified by the given packet type and the given id. * If id is <= 0, then we don't try and match the ids with packets. * If pp is not NULL, then we call it on the event (pp(conn,e,arg)), * to see if it is a match. * If we find an event with a match, given the above criteria, then * we copy the event into the user structure and if 'remove' is non-zero * we remove the event from the event list. */ static DXLError DXLGetPacket(DXLConnection *conn, DXLPacketTypeEnum type, int requestId, DXLEvent *event, int remove, DXLPacketPredicate pp, const void *arg) { DXLEvent *e; e = DXLFindEventInList(conn,conn->events,type,requestId, pp, arg); while (!e) { e = conn->lastEvent; if (! _dxl_WaitForReadable(conn)) return ERROR; if (! _dxl_ReadFromSocket(conn)) return ERROR; if (!e) e = conn->events; else e = e->next; #if 0 PrintEventList(e); fprintf(stderr, "\n"); #endif e = DXLFindEventInList(conn,e,type,requestId, pp, arg); } DXLCopyEvent(event, e); if (remove) { RemoveEvent(conn, e); DXLFreeEvent(e); } return OK; } static int MatchString(DXLConnection *conn, DXLEvent *e, void *arg) { char *str = (char*)arg; if (!str || strstr(e->contents,str)) return 1; else return 0; } /* * Find and read until a packet with the given type, packet id, and string * contained in the message is found. This may block on the socket. * Return the found event in the given event, and remove the event * from the event list. */ DXLError DXLGetPacketIdString(DXLConnection *conn, DXLPacketTypeEnum type, int requestId, const char *str, DXLEvent *event) { return DXLGetPacket(conn,type, requestId,event, 1, MatchString,str); } /* * Find and read until a packet with the given type and string * contained in the message is found. This may block on the socket. * Return the found event in the given event, and remove the event * from the event list. */ DXLError DXLGetPacketString(DXLConnection *conn, DXLPacketTypeEnum type, const char *str, DXLEvent *event) { return DXLGetPacket(conn,type,-1,event, 1, MatchString,str); } /* * Find a packet in the current event list with the given type and string * contained in the message. Do not block on the socket. * Return the found event in the given event, and do not remove the event * from the event list. Return 0 if a matching packet was not found, * otherwise non-zero. */ int DXLCheckPacketString(DXLConnection *conn, DXLPacketTypeEnum type, const char *str, DXLEvent *event) { return DXLCheckPacket(conn,type,-1,event, MatchString,str); } /* * Find and read until a packet with the given type and string contained * in the message is found. * Return the found event in the given event, and do not remove the event * from the event list. */ DXLError DXLWaitPacketString(DXLConnection *conn, DXLPacketTypeEnum type, const char *str, DXLEvent *event) { return DXLGetPacket(conn,type,-1,event, 0, MatchString,str); } /* * Find and read until a packet with the given type and id is found. * Return the found event in the given event, and remove the event * from the event list. */ DXLError DXLGetPacketId(DXLConnection *conn, DXLPacketTypeEnum type, int id, DXLEvent *event) { return DXLGetPacket(conn,type,id,event, 1, NULL,NULL); } DXLError DXLPeekPacket(DXLConnection *conn, DXLEvent *event) { if (conn->nEvents == 0) _dxl_ReadFromSocket(conn); if (conn->nEvents == 0) return ERROR; DXLCopyEvent(event, conn->events); return OK; } static int _dxl_IsReadable(DXLConnection *conn) { #ifndef OS2 fd_set fds; #endif #ifdef hp700 int width = MAXFUPLIM; #else #ifdef aviion int width = NOFILE; #else #ifdef solaris int width = FD_SETSIZE; #else #ifdef DXD_HAS_WINSOCKETS int width = FD_SETSIZE; #else #ifndef OS2 int width = getdtablesize(); #endif #endif #endif #endif #endif int retval = 0; struct timeval to; if (conn->fd < 0) return 0; #if !defined(OS2) && !defined(DXD_HAS_WINSOCKETS) FD_ZERO(&fds); FD_SET(conn->fd, &fds); to.tv_sec = 0; to.tv_usec = 0; /* this will restart select if it has been interrupted by a signal */ SELECT_INTR(width, (SELECT_ARG_TYPE)&fds, NULL, NULL, &to, retval); #endif #ifdef OS2 retval = select(&conn->fd, 1, 0, 0, 0); #endif #ifdef DXD_HAS_WINSOCKETS to.tv_sec = 0; to.tv_usec = 0; FD_ZERO(&fds); FD_SET(conn->fd, &fds); retval = select(conn->fd, &fds, NULL, NULL, &to); #endif if (retval > 0) return TRUE; if (retval == 0) return FALSE; /* retval < 0 */ _dxl_InvalidateSocket (conn); return FALSE; } int DXLIsMessagePending(DXLConnection *conn) { return (conn->nEvents > 0) || _dxl_IsReadable(conn); } static int _dxl_WaitForReadable(DXLConnection *conn) { int retval; #ifndef OS2 fd_set fds; #endif #ifdef hp700 int width = MAXFUPLIM; #else #ifdef aviion int width = NOFILE; #else #ifdef solaris int width = FD_SETSIZE; #else #ifdef DXD_HAS_WINSOCKETS int width = FD_SETSIZE; #else #ifndef OS2 int width = getdtablesize(); #endif #endif #endif #endif #endif if (conn->fd < 0) return 0; #if !defined(OS2) && !defined(DXD_HAS_WINSOCKETS) FD_ZERO(&fds); FD_SET(conn->fd, &fds); /* this will restart select if it was interrupted by a signal */ SELECT_INTR(width, (SELECT_ARG_TYPE)&fds, NULL, NULL, NULL, retval); if(retval < 0) goto error; #endif #ifdef OS2 if (select(&conn->fd, 1, 0, 0, -1)<0) goto error; #endif #ifdef DXD_HAS_WINSOCKETS FD_ZERO(&fds); FD_SET(conn->fd, &fds); width = select(width, &fds, NULL, NULL, NULL); if (width < 0) goto error; #endif return 1; error: _dxl_InvalidateSocket(conn); return 0; } #if 0 static int lock_me = 0; #endif static int _dxl_ReadFromSocket(DXLConnection *conn) { int id, typei; char type[8]; int nbytes; char buffer[4096]; char *start; int nRemaining; if (conn->fd < 0) return 0; while (_dxl_IsReadable(conn)) { int nread; nRemaining = conn->nLeftOver; if (nRemaining) { memcpy (buffer, conn->leftOver, nRemaining); nread = read(conn->fd, buffer+nRemaining, 1024); } else { nread = read(conn->fd, buffer, 1024); } if (nread <= 0) goto error; nRemaining+= nread; buffer[nRemaining] = '\0'; start = buffer; /* * We used to check done here, but now we'll just keep reading * from start until there isn't anything left to read. */ while (1) { int len, items; DXLEvent *event; char msgbuf[4096]; int msgbytes_in_buffer; int bytes_used; short message_completed; /* * sscanf() leaves 'len' unset if the last '|' was not seen */ len = -1; items = sscanf(start, "|%d|%[^|]|%d|%n", &id, type, &nbytes, &len); /* * Perform 2 checks to see if we have enough bytes in the buffer * to form a complete message. First check to see if the sscanf * completed. Then check to see if the number of bytes resulting * completes a message. In order to complete a message it has * to get the trailing |\n. */ if ((items != 3) || (len == -1)) { message_completed = 0; } else { msgbytes_in_buffer = nRemaining - len; if ((nbytes+2) > msgbytes_in_buffer) { message_completed = 0; } else { message_completed = 1; } } if (!message_completed) { if (nRemaining >= conn->allocdLeftOver) { free(conn->leftOver); /* malloc 1.5 times nRemaining rounded to the nearest 16 */ conn->allocdLeftOver = 16 + ((nRemaining + (nRemaining>>1)) & (~15)); conn->leftOver = malloc(conn->allocdLeftOver); if (!conn->leftOver) goto error; } strncpy(conn->leftOver, start, nRemaining); conn->nLeftOver = nRemaining; break; } for (typei = 1; typei < _DXLNumPackets; typei++) if (! strcmp(type, _DXLPacketTypes[typei])) break; if (typei == _DXLNumPackets) { fprintf (stderr, "%s[%d] packet error\n", __FILE__,__LINE__); goto error; } bytes_used = (len + nbytes + 2); /* 2 = "|\n" */ strncpy(msgbuf, start+len, nbytes + 2); if (bytes_used > nRemaining) goto error; if (msgbuf[nbytes] != '|') { fprintf (stderr, "%s[%d]packet error\n", __FILE__,__LINE__); goto error; } nRemaining -= bytes_used; start += bytes_used; msgbuf[nbytes] = '\0'; event = DXLNewEvent(id, typei,msgbuf); if (!event) goto error; if (conn->debugMessaging) { fprintf(stderr, "Recving <- "); PrintEvent(event); } AppendEvent(conn,event); } } return 1; error: _dxl_InvalidateSocket(conn); return 0; } DXLError DXLSendUnpacketized(DXLConnection *conn, const char *string) { int length, r; char *newString; if (conn->fd < 0) return ERROR; length = STRLEN(string) + 1; newString = malloc((length + 1) * sizeof(char)); strcpy(newString, string); newString[length-1] = '\n'; newString[length] = '\0'; if (conn->debugMessaging) fprintf(stderr, "Sending -> %s", newString); #ifdef DEBUG_MESSAGES if (first) { first = 0; dbg = fopen("debug", "w"); } else dbg = fopen("debug", "a"); fprintf(dbg, "Unpacketized --> %s", newString); fclose(dbg); #endif if (write(conn->fd, newString, length) != length) r = ERROR; else r = OK; free(newString); return r; } DXLError DXLSendImmediate(DXLConnection *conn, const char *string) { int length, r; char *newString; if (conn->fd < 0) return ERROR; length = STRLEN(string) + 1; newString = malloc((length + 1) * sizeof(char)); strcpy(newString, "$"); strcat(newString, string); r = DXLSendUnpacketized(conn,newString); if (r == OK && conn->synchronous) DXLSync(conn); free(newString); return r; } DXLError DXLHandlePendingMessages(DXLConnection *conn) { DXLProcessEventList(conn); while (_dxl_IsReadable(conn)) { _dxl_ReadFromSocket(conn); DXLProcessEventList(conn); } return OK; } static void PrintEvent(DXLEvent *e) { fprintf(stderr, "id %d type %d %s\n", e->serial, e->type, e->contents); } static void PrintEventList(DXLEvent *e) { DXLEvent *event; for (event = e; event ; event = event->next) PrintEvent(event); } int DXLSetMessageDebugging(DXLConnection *conn, int on) { int oldval = conn->debugMessaging; conn->debugMessaging = on; return oldval; } /* DW D Watson added to end of file */ DXLError exDXLBeginMacroDefinition(DXLConnection *conn, const char *mhdr) { int l = STRLEN(mhdr); int sts = 0; DXLPacketTypeEnum ptype; if (conn->dxuiConnected) { return ERROR; } else { conn->macroDef = TRUE; ptype = PACK_MACRODEF; sts = DXLSendPacket(conn,ptype,0); if (sts < 0) return ERROR; DXLSendUnpacketized(conn, mhdr); DXLSendUnpacketized(conn, "{\n"); } return OK; } DXLError exDXLEndMacroDefinition(DXLConnection *conn) { int sts = 0; DXLPacketTypeEnum ptype; if (conn->dxuiConnected) { return ERROR; } else { if (!conn->macroDef) { fprintf(stderr,"exDXLEndMacroDefinition: not valid before exDXLBeginMacroDefinition()\n"); return ERROR; } /* endif */ conn->macroDef = FALSE; sts = DXLSendUnpacketized(conn,"}\n|\n"); if (sts < 0) return ERROR; } if (conn->synchronous) if (!DXLSync(conn)) return ERROR; return OK; }