aboutsummaryrefslogtreecommitdiff
path: root/vendor/x11iraf/ximtool/ism.c
diff options
context:
space:
mode:
authorJoe Hunkeler <jhunkeler@gmail.com>2015-08-11 16:51:37 -0400
committerJoe Hunkeler <jhunkeler@gmail.com>2015-08-11 16:51:37 -0400
commit40e5a5811c6ffce9b0974e93cdd927cbcf60c157 (patch)
tree4464880c571602d54f6ae114729bf62a89518057 /vendor/x11iraf/ximtool/ism.c
downloadiraf-osx-40e5a5811c6ffce9b0974e93cdd927cbcf60c157.tar.gz
Repatch (from linux) of OSX IRAF
Diffstat (limited to 'vendor/x11iraf/ximtool/ism.c')
-rw-r--r--vendor/x11iraf/ximtool/ism.c706
1 files changed, 706 insertions, 0 deletions
diff --git a/vendor/x11iraf/ximtool/ism.c b/vendor/x11iraf/ximtool/ism.c
new file mode 100644
index 00000000..ec9d6b62
--- /dev/null
+++ b/vendor/x11iraf/ximtool/ism.c
@@ -0,0 +1,706 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/tcp.h>
+#include <fcntl.h>
+#include <sys/un.h>
+
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <Tcl/tcl.h>
+#include <Obm.h>
+#include <ObmW/Gterm.h>
+#include "ximtool.h"
+
+/*
+ * ISM.C -- IRAF Image Support Module (ISM) routines.
+ *
+ * xim_ismOpen (xim)
+ * xim_ismClose (xim)
+ *
+ * ism_connectClient (chan_port, source, id)
+ * ism_disconnectClient (chan)
+ * ism_io (chan, fd_addr, id_addr)
+ *
+ * ism_execute (xim, task)
+ * ism_evaluate (object, command)
+ *
+ * ism_openSocket (path)
+ * ism_read (fd, ptr, nbytes)
+ * ism_write (fd, ptr, nbytes)
+ */
+
+
+
+#define CALLBACK 0
+#define QUIT 1
+#define SEND 2
+#define READY 3
+
+
+#define MAXCONN 5
+#define MAX_TRY 5
+
+void xim_ismOpen(), xim_ismClose();
+IsmModule ismNameToPtr();
+
+static void ism_connectClient(), ism_disconnectClient(), ism_io();
+static int ism_read(), ism_write(), ism_type(), ism_parseSend();
+static int ism_openSocket();
+static IsmIoChanPtr ism_getChannel();
+static char *ism_parse();
+
+static int ism_debug = 0;
+extern int errno;
+
+
+/* WCS/Pixel ISM client callbacks.
+ */
+#define WCSPIX_CMD DEF_ISM_CMD
+
+void wcspix_connect(), wcspix_disconnect(), wcspix_command();
+
+
+/* Definitions for the supported ISM Modules. */
+int ism_nmodules = 0;
+
+ismModule ism_modules[] = {
+ {"wcspix", WCSPIX_CMD, wcspix_connect, wcspix_disconnect, wcspix_command, 0},
+};
+
+
+
+/* XIM_ISMOPEN -- Initialize the ISM protocol module and ready the module to
+ * accept client connections and begin processing client requests. This
+ * procedure only opens the ISM connection port, once clients are connected
+ * they negotiate for a separate channel and I/O resumes there. Once,
+ * connected, the client disconnects from this port and it is free to be used
+ * by another client. Unix sockets are used since we only want to connect to
+ * local processes and we want to use the uid for a unique address.
+ */
+void
+xim_ismOpen (xim)
+register XimDataPtr xim;
+{
+ register int s = 0;
+ register IsmIoChanPtr chan = &(xim->ism_chan);
+ char path[SZ_FNAME], *ism_path;
+
+
+ if (getenv("DEBUG_ISM") != NULL)
+ ism_debug = atoi(getenv("DEBUG_ISM"));
+
+ /* Setting the addr to "none" or the null string disables ISM
+ * socket support.
+ */
+ if (!xim->ism_addr[0] || strcmp(xim->ism_addr,"none")==0)
+ return ;
+
+ /* Get path to be used for the unix domain socket. */
+ if ((ism_path = getenv ("ISMDEV")) != NULL) {
+ char *dev = ism_path;
+
+ while (1) {
+ if (*dev == (char)NULL) {
+ dev = ism_path;
+ break;
+ } else if (*dev == ':') {
+ ++dev;
+ break;
+ }
+ dev++;
+ }
+ sprintf (path, dev, getuid());
+ } else
+ sprintf (path, xim->ism_addr, getuid());
+ unlink (path);
+
+ if ((s = ism_openSocket (path))) {
+ /* Fill in i/o channel descriptor. */
+ chan->xim = (XtPointer) xim;
+ chan->datain = s;
+ chan->dataout = s;
+ chan->connected = 0;
+ strncpy (chan->name, "", SZ_FNAME);
+ strncpy (chan->path, path, SZ_FNAME);
+
+ if (ism_debug) printf ("opened connection on '%s' %d\n", path, s);
+
+ /* Register connectClient callback. */
+ chan->id = xim_addInput (xim, s, ism_connectClient,
+ (XtPointer)chan);
+ }
+
+ ism_nmodules = XtNumber (ism_modules);
+
+ /* Set the default ISM path in the GUI. */
+ sprintf (path, "wcspix_cmd {%s}", DEF_ISM_CMD);
+ wcspix_message (xim, path);
+}
+
+
+/* XIM_ISMCLOSE -- Close down the ISM protocol module. Disconnect all connected
+ * clients and close the port.
+ */
+void
+xim_ismClose (xim)
+register XimDataPtr xim;
+{
+ register IsmIoChanPtr chan = &(xim->ism_chan);
+ register int i;
+
+ /* Send a 'quit' message to all connected clients. */
+ for (i=0, chan=NULL; i < XtNumber(xim->ism_client); i++) {
+ chan = &xim->ism_client[i];
+ if (chan->id) {
+ xim_removeInput (xim, chan->id);
+ chan->id = NULL;
+ }
+
+ if (chan->connected)
+ ism_write (chan->dataout, "quit", 4);
+
+ close (chan->datain);
+ }
+
+ /* Close the ISM request socket. */
+ chan = &(xim->ism_chan);
+ if (chan->id) {
+ xim_removeInput (xim, chan->id);
+ chan->id = NULL;
+ }
+ close (chan->datain);
+ unlink (chan->path);
+}
+
+
+/* ISMNAMETOPTR -- Utility procedure to lookup an ISM struct pointer given
+ * the ISM name.
+ */
+IsmModule
+ismNameToPtr (name)
+char *name;
+{
+ IsmModule ism;
+ register int i;
+
+ for (i=0; i < ism_nmodules; i++) {
+ ism = &ism_modules[i];
+ if (strcmp (name, ism->name) == 0)
+ return (ism);
+ }
+ return ((IsmModule)NULL);
+}
+
+
+/*******************************
+ * TRANSPORT LAYER PROCEDURES
+ ******************************/
+
+
+/* ISM_CONNECTCLIENT -- Called when a client has attempted a connection on
+ * a socket port. Accept the connection and set up a new i/o channel to
+ * communicate with the new client.
+ */
+static void
+ism_connectClient (chan, source, id)
+IsmIoChanPtr chan;
+int *source;
+XtPointer id;
+{
+ register XimDataPtr xim = (XimDataPtr) chan->xim;
+ register int s;
+
+ /* Accept connection. */
+ if ((s = accept ((int)*source, (struct sockaddr *)0, (int *)0)) < 0)
+ return;
+ /*if (fcntl (s, F_SETFL, O_RDWR|O_NDELAY) < 0) {*/
+ if (fcntl (s, F_SETFL, O_NDELAY) < 0) {
+ close (s);
+ return;
+ }
+
+ /* Fill in the ISM i/o channel descriptor. */
+ chan->datain = s;
+ chan->dataout = s;
+ chan->connected = 1;
+ chan->id = xim_addInput (xim, s, ism_io, (XtPointer)chan);
+}
+
+
+/* ISM_DISCONNECTCLIENT -- Called to close a client connection when EOF is
+ * seen on the input port. Close the connection and free the channel
+ * descriptor.
+ */
+static void
+ism_disconnectClient (chan)
+register IsmIoChanPtr chan;
+{
+ close (chan->datain);
+ if (chan->id) {
+ xim_removeInput (chan->xim, chan->id);
+ chan->connected = 0;
+ chan->id = NULL;
+ }
+}
+
+
+/* ISM_IO -- Xt file i/o callback procedure, called when there is input
+ * pending on the data stream to the ximtool client.
+ */
+static void
+ism_io (chan, fd_addr, id_addr)
+IsmIoChanPtr chan;
+int *fd_addr;
+XtInputId *id_addr;
+{
+ register XimDataPtr xim = (XimDataPtr) chan->xim;
+ register IsmModule ism;
+ IsmIoChanPtr new_chan;
+ int datain = *fd_addr;
+ int dataout = chan->dataout;
+ int s, n, ip, type, count = 0;
+ char name[SZ_FNAME], path[SZ_FNAME];
+ char message[2*SZ_ISMBUF+1];
+ char buf[SZ_ISMBUF+1];
+ char *text = NULL;
+ static int incomplete_msg = 0;
+ static int null_count = 0;
+ static long pkt=0, nread=0;
+
+
+ /* Read the message. */
+ bzero (buf, SZ_ISMBUF+1);
+ count = read (datain, buf, SZ_ISMBUF);
+ nread += count;
+ if (count > SZ_ISMBUF || count == 0) {
+ if (null_count++ > MAX_TRY) {
+ null_count = 0;
+ ism_disconnectClient (chan);
+ }
+ return;
+ }
+
+ /* Build up a complete message buffer including any incomplete
+ * text from the last read.
+ */
+ bzero (message, 2*SZ_ISMBUF+1);
+ if (chan->msgbuf[0]) {
+ n = strlen(chan->msgbuf);
+
+ memmove (message, chan->msgbuf, n);
+ memmove (&message[n], buf, count);
+ count += n;
+ } else {
+ bcopy (buf, message, count);
+ }
+ bzero (chan->msgbuf, SZ_ISMBUF);
+
+ if (ism_debug >= 2)
+ printf("\nism_io: nread=%d pkt=%d count=%d n=%d\n",nread,pkt++,count,n);
+
+
+ ip = 0;
+ incomplete_msg = 0;
+ while (text = ism_parse (message, &ip, &incomplete_msg, count)) {
+
+ if (incomplete_msg) {
+ /* Save the incomplete message to the buffer for later parsing.
+ */
+ if (ism_debug >= 2)
+ printf ("INCOMPLETE '%s' ip=%d len=%d\n", text,ip,strlen(text));
+ strcpy (chan->msgbuf, text);
+ break;
+ }
+
+
+ /* Messages
+ *
+ * CALLBACK - Negotiate a connection on another socket
+ * QUIT - client is shutting down and disconnecting
+ * SEND - send a message to another object
+ * READY - client is ready to begin processing
+ */
+ switch (ism_type (text)) {
+ case CALLBACK:
+
+ /* Get the requesting client's name. */
+ sscanf (text, "connect %s", name);
+
+ /* Get a new i/o channel. */
+ if (new_chan = ism_getChannel (xim)) {
+
+ /* Get path to be used for the unix domain socket. */
+ sprintf (path, DEF_ISM_TEMPLATE, getuid(), new_chan->id);
+ unlink (path);
+
+ if (ism_debug)
+ printf("ism_io: CONNECT '%s' on socket '%s'\n", name, path);
+
+ if ((s = ism_openSocket (path))) {
+ /* Fill in i/o channel descriptor. */
+ new_chan->xim = (XtPointer) xim;
+ new_chan->datain = s;
+ new_chan->dataout = s;
+ new_chan->connected = 0;
+ strncpy (new_chan->path, path, SZ_FNAME);
+ strncpy (new_chan->name, name, SZ_FNAME);
+
+ /* Register connectClient callback. */
+ new_chan->id = xim_addInput (xim, s, ism_connectClient,
+ (XtPointer)new_chan);
+ }
+ }
+
+ /* Create a new OBM object for the client, save the client name
+ * one the channel descriptor so we'll know who disconnected.
+ */
+ if (ismObjects (name) == 0)
+ obmNewObject (xim->obm, name, "Client", NULL, NULL, 0);
+ strcpy (chan->name, name);
+
+ /* Now tell the client to call us back on the new channel */
+ sprintf (buf, "connect %s", path);
+ if (ism_debug) printf ("ism_io: msg '%s'\n", buf);
+ ism_write (dataout, buf, strlen(buf));
+
+ /* Hang up the current connection. */
+ if (ism_debug) printf ("CALLBACK: disconnecting '%s'\n",chan->path);
+ ism_disconnectClient (chan);
+
+ break;
+
+ case READY:
+ if (ism_debug) printf ("READY: ready '%s'\n", chan->name);
+
+ /* Execute the ISM startup callback to initialize it.
+ */
+ for (n=0; n < XtNumber (ism_modules) ; n++) {
+ ism = &ism_modules[n];
+ if (strcmp (chan->name, ism->name) == 0) {
+ ism->connected = 1;
+ ism->chan = chan;
+ (*ism->startupCB) (xim, ism);
+ }
+ }
+ break;
+
+ case QUIT:
+ if (ism_debug)
+ printf ("QUIT: quit '%s' on '%s'\n", chan->name, chan->path);
+
+ /* Execute the ISM shutdown callback.
+ */
+ for (n=0; n < XtNumber (ism_modules) ; n++) {
+ ism = &ism_modules[n];
+ if (strcmp (chan->name, ism->name) == 0) {
+ (*ism->shutdownCB) (xim, ism);
+ ism->connected = 0;
+ ism->chan = (IsmIoChanPtr) NULL;
+ }
+ }
+ ism_disconnectClient (chan);
+
+ break;
+
+ case SEND:
+ /* Deliver the message to the named object.
+ */
+ ism_parseSend (text, name, buf);
+ if (ism_debug >= 3)
+ printf ("SEND: len=%d '%s'->'%.45s'\n", strlen(buf), name, buf);
+ xim_message (xim, name, buf);
+ break;
+
+ default:
+ fprintf (stderr, "ism_io: Unknown message '%s'\n", text);
+ break;
+ }
+ }
+}
+
+
+/* ISM_PARSE -- Parse the client message, returning the type as the function
+ * value.
+ */
+static char *
+ism_parse (msg, ip, incomplete, maxch)
+char *msg;
+int *ip;
+int *incomplete;
+int maxch;
+{
+ register int j, i = *ip, count=0;
+ char text[SZ_ISMBUF+1];
+
+ if (msg[*ip] == '\0') {
+/* *incomplete = 1;*/
+ return (NULL);
+ }
+
+ /* Zero the retrun buffer and skip any leading NULL input chars. */
+ bzero (text, SZ_ISMBUF+1);
+ while ((msg[i] == '\0') && i < maxch) i++;
+
+ /* Copy the message up to the terminating NULL. */
+ for (j=0; (msg[i] != '\0') && (i < maxch); )
+ text[j++] = msg[i++];
+ text[j] = msg[i];
+
+ /* If we hit the end of the input buffer without seeing a null
+ * then we're parsing a partial message.
+ */
+ *incomplete = (i < maxch ? 0 : 1);
+
+ /* update the position ptr */
+ while ((msg[i] == '\0') && i < maxch) i++;
+ *ip = i;
+
+ if (ism_debug >= 3 && text[0]) {
+ printf ("ism_parse: ip=%d msg=%d i=%d j=%d inc=%d\n",
+ *ip, msg[i], i, j, *incomplete);
+ }
+
+
+ return (text[0] ? text : NULL);
+}
+
+
+/* ISM_TYPE -- Determine the message type.
+ */
+static int
+ism_type (message)
+char *message;
+{
+ register char *ip;
+
+ for (ip=message; isspace(*ip); ip++) ; /* skip whitespace */
+
+ if (strncmp (ip, "connect", 7) == 0)
+ return (CALLBACK);
+ else if (strncmp (ip, "quit", 4) == 0)
+ return (QUIT);
+ else if (strncmp (ip, "ready", 4) == 0)
+ return (READY);
+ else if (strncmp (ip, "send", 4) == 0)
+ return (SEND);
+ else
+ return (ERR);
+}
+
+
+/* ISM_PARSESEND -- Parse the client SEND message.
+ */
+static int
+ism_parseSend (msg, object, text)
+char *msg;
+char *object;
+char *text;
+{
+ register int i=0, ip=4, count=0;
+
+ /* skip leading whitespace */
+ for ( ; isspace(msg[ip]) && msg[ip]; ip++) ;
+
+ /* Get the object name */
+ for (i=0; !isspace(msg[ip]) && msg[ip]; i++, ip++)
+ object[i] = msg[ip];
+ object[i] = '\0';
+
+ for ( ; msg[ip] != '{'; ip++) ; /* skip to open bracket */
+
+ /* Get the message text. Count brackets so we can pass
+ * Tcl code properly.
+ */
+ for (i=0, ip++; msg[ip]; i++, ip++) {
+ text[i] = msg[ip];
+ if (msg[ip] == '{')
+ count++;
+ else if (msg[ip] == '}') {
+ if (count <= 0) { ip++; break; }
+ count--;
+ }
+ }
+ text[i] = '\0';
+}
+
+
+/* ISM_EVALUATE -- Evaluate a command for the named object. Used by the
+ * clientEvaluate() method. Can also be used by the rest of ximtool to
+ * send messages to the ISM, which will be ignored if the client is not
+ * connected.
+ */
+ism_evaluate (xim, object, command)
+register XimDataPtr xim;
+char *object;
+char *command;
+{
+ register IsmIoChanPtr chan;
+ register int i=0;
+ register int len = strlen(command) + 1;
+ char *buf = XtCalloc (len+1, sizeof(char));
+
+ for (i=0; i < XtNumber (xim->ism_client); i++) {
+ chan = &xim->ism_client[i];
+ if (chan->connected && strcmp (chan->name, object) == 0) {
+ sprintf (buf, "%s\0", command);
+ len = strlen (buf) + 1; /* +1 to send the NULL */
+ ism_write (chan->dataout, buf, len);
+ if (ism_debug >= 2) printf("writing %d bytes: '%s'\n", len,buf);
+ break;
+ }
+ }
+
+ XtFree ((char *)buf);
+}
+
+
+/* ISM_MESSAGE -- Convenience wrapper for the evaluate procedure.
+ */
+ism_message (xim, object, command)
+register XimDataPtr xim;
+char *object, *command;
+{
+ ism_evaluate (xim, object, command);
+}
+
+
+/* ISM_OPENSOCKET -- Open a unix socket on the named path.
+ */
+static int
+ism_openSocket (path)
+char *path; /* path to the socket */
+{
+ int addrlen, s = 0, on = 1;
+ struct sockaddr_un sockaddr;
+
+ if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) < 0)
+ goto err;
+
+ memset ((void *)&sockaddr, 0, sizeof(sockaddr));
+ sockaddr.sun_family = AF_UNIX;
+ strcpy (sockaddr.sun_path, path);
+ addrlen = sizeof(sockaddr) - sizeof(sockaddr.sun_path) + strlen(path);
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on,sizeof(on)) < 0)
+ goto err;
+/*
+ if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,sizeof(on)) < 0)
+ goto err;
+*/
+ if (bind (s, (struct sockaddr *)&sockaddr, addrlen) < 0)
+ goto err;
+
+ if (listen (s, MAXCONN) < 0) {
+err: fprintf (stderr, "ximtool: can't open ISM socket on %s, errno=%d\n",
+ path, errno);
+ if (s)
+ close (s);
+ return (0);
+ }
+
+ return (s);
+}
+
+
+
+/* ISM_GETCHANNEL --- Get an ISM i/o channel descriptor.
+ */
+static IsmIoChanPtr
+ism_getChannel (xim)
+register XimDataPtr xim;
+{
+ register IsmIoChanPtr chan;
+ register int i;
+
+ for (i=0; i < XtNumber(xim->ism_client); i++) {
+ if (!xim->ism_client[i].connected) {
+ xim->ism_client[i].id = (XtPointer) i;
+ return (&xim->ism_client[i]);
+ }
+ }
+
+ return (NULL);
+}
+
+
+/* ISMOBJECTS -- Add the named client to the list of known objects, or
+ * return zero if this is a new object. We keep a list so we don't keep
+ * creating the same object in the OBM each time a client connects.
+ */
+ismObjects (name)
+char *name;
+{
+ static char objects[SZ_LINE] = "";
+
+ if (strstr (objects, name) == NULL) {
+ strcat (objects, name);
+ strcat (objects, "|\0");
+ return (0);
+ } else
+ return (1);
+}
+
+
+/* ISM_READ -- Read exactly "n" bytes from a descriptor.
+ */
+
+static int
+ism_read (fd, vptr, nbytes)
+int fd;
+void *vptr;
+int nbytes;
+{
+ char *ptr = vptr;
+ int nread = 0, nleft = nbytes, nb = 0;
+
+ while (nleft > 0) {
+ if ( (nb = read(fd, ptr, nleft)) < 0) {
+ if (errno == EINTR)
+ nb = 0; /* and call read() again */
+ else
+ return(-1);
+ } else if (nb == 0)
+ break; /* EOF */
+ nleft -= nb;
+ ptr += nb;
+ nread += nb;
+ }
+
+ return (nread); /* return no. of bytes read */
+}
+
+
+/* ISM_WRITE -- Write exactly "n" bytes to a descriptor.
+ */
+
+static int
+ism_write (fd, vptr, nbytes)
+int fd;
+void *vptr;
+int nbytes;
+{
+ char *ptr = vptr;
+ int nwritten = 0, nleft = nbytes, nb = 0;
+
+ /* Send the bytecount first.
+ if ((nb = write (fd, &nleft, sizeof (int))) <= 0)
+ return (-1);
+ */
+
+ /* Now send the message. */
+ while (nleft > 0) {
+ if ( (nb = write(fd, ptr, nleft)) <= 0) {
+ if (errno == EINTR)
+ nb = 0; /* and call write() again */
+ else
+ return (-1); /* error */
+ }
+ nleft -= nb;
+ ptr += nb;
+ nwritten += nb;
+ }
+
+ return (nwritten);
+}