INDI::BaseDevice * INDI::BaseClientQt::findDev (XMLEle * root, int create, char * errmsg) { XMLAtt * ap; INDI::BaseDevice * dp; char * dn; /* get device name */ ap = findXMLAtt (root, "device"); if (!ap) { snprintf(errmsg, MAXRBUF, "No device attribute found in element %s", tagXMLEle(root)); return (NULL); } dn = valuXMLAtt(ap); if (*dn == '\0') { snprintf(errmsg, MAXRBUF, "Device name is empty! %s", tagXMLEle(root)); return (NULL); } dp = findDev(dn, errmsg); if (dp) return dp; /* not found, create if ok */ if (create) return (addDevice (root, errmsg)); snprintf(errmsg, MAXRBUF, "INDI: <%s> no such device %s", tagXMLEle(root), dn); return NULL; }
/* put Msg mp on queue of each driver snooping dev/name. * if BLOB always honor current mode. */ void q2SDrivers (int isblob, const char *dev, const char *name, Msg *mp, XMLEle *root) { DvrInfo *dp; for (dp = dvrinfo; dp < &dvrinfo[ndvrinfo]; dp++) { Property *sp = findSDevice (dp, dev, name); /* nothing for dp if not snooping for dev/name or wrong BLOB mode */ if (!sp) continue; if ((isblob && sp->blob==B_NEVER) || (!isblob && sp->blob==B_ONLY)) continue; /* ok: queue message to this device */ mp->count++; pushFQ (dp->msgq, mp); if (verbose > 1) { fprintf (stderr, "%s: Driver %s: queuing snooped <%s device='%s' name='%s'>\n", indi_tstamp(NULL), dp->name, tagXMLEle(root), findXMLAttValu (root, "device"), findXMLAttValu (root, "name")); } } }
/* put Msg mp on queue of each driver responsible for dev, or all drivers * if dev not specified. */ void q2RDrivers (const char *dev, Msg *mp, XMLEle *root) { int sawremote = 0; DvrInfo *dp; /* queue message to each interested driver. * N.B. don't send generic getProps to more than one remote driver, * otherwise they all fan out and we get multiple responses back. */ for (dp = dvrinfo; dp < &dvrinfo[ndvrinfo]; dp++) { int isremote = (dp->pid == REMOTEDVR); if (dp->active == 0) continue; //if (dev[0] && dp->dev[0] && strcmp (dev, dp->dev)) if (dev[0] && isDeviceInDriver(dev, dp) == 0) continue; /* driver known to not support this dev */ if (!dev[0] && isremote && sawremote) continue; /* already sent generic to another remote */ if (isremote) sawremote = 1; /* ok: queue message to this driver */ mp->count++; pushFQ (dp->msgq, mp); if (verbose > 1) fprintf (stderr, "%s: Driver %s: queuing responsible for <%s device='%s' name='%s'>\n", indi_tstamp(NULL), dp->name, tagXMLEle(root), findXMLAttValu (root, "device"), findXMLAttValu (root, "name")); } }
/* read more from the given client, send to each appropriate driver when see * xml closure. also send all newXXX() to all other interested clients. * shut down client if any trouble. */ static void clientMsg (ClInfo *cp) { char buf[BUFSZ]; int i, nr; /* read client */ nr = read (cp->s, buf, sizeof(buf)); if (nr < 0) { fprintf (stderr, "Client %d: %s\n", cp->s, strerror(errno)); shutdownClient (cp); return; } if (nr == 0) { if (verbose) fprintf (stderr, "Client %d: read EOF\n", cp->s); shutdownClient (cp); return; } if (verbose > 2) fprintf (stderr, "Client %d: read %d:\n%.*s", cp->s, nr, nr, buf); /* process XML, sending when find closure */ for (i = 0; i < nr; i++) { char err[1024]; XMLEle *root = readXMLEle (cp->lp, buf[i], err); if (root) { char *roottag = tagXMLEle(root); if (verbose > 1) fprintf (stderr, "Client %d: read %s\n", cp->s, xmlLog(root)); /* record BLOB message locally, others go to matching drivers */ if (!strcmp (roottag, "enableBLOB")) { cp->blob = crackBLOB (pcdataXMLEle(root)); delXMLEle (root); } else { char *dev = findXMLAttValu (root, "device"); /* snag interested devices */ if (!strcmp (roottag, "getProperties")) addClDevice (cp, dev); /* send message to driver(s) responsible for dev */ send2Drivers (root, dev); /* echo new* commands back to other clients, else done */ if (!strncmp (roottag, "new", 3)) send2Clients (cp, root, dev); /* does delXMLEle */ else delXMLEle (root); } } else if (err[0]) fprintf (stderr, "Client %d: %s\n", cp->s, err); } }
/* return pointer to static string containing tag, device and name attributes * of the given xml */ static char * xmlLog (XMLEle *root) { static char buf[256]; sprintf (buf, "%.*s %.*s %.*s", sizeof(buf)/3-2, tagXMLEle(root), sizeof(buf)/3-2, findXMLAttValu(root,"device"), sizeof(buf)/3-2, findXMLAttValu(root,"name")); return (buf); }
/* pull out device and name attributes from root. * return 0 if ok else -1 with reason in msg[]. */ int crackDN (XMLEle *root, char **dev, char **name, char msg[]) { XMLAtt *ap; ap = findXMLAtt (root, "device"); if (!ap) { sprintf (msg, "%s requires 'device' attribute", tagXMLEle(root)); return (-1); } *dev = valuXMLAtt(ap); ap = findXMLAtt (root, "name"); if (!ap) { sprintf (msg, "%s requires 'name' attribute", tagXMLEle(root)); return (-1); } *name = valuXMLAtt(ap); return (0); }
/* print key attributes and values of the given xml to stderr. */ void traceMsg (XMLEle *root) { static const char *prtags[] = { "defNumber", "oneNumber", "defText", "oneText", "defSwitch", "oneSwitch", "defLight", "oneLight", }; XMLEle *e; const char *msg, *perm, *pcd; unsigned int i; /* print tag header */ fprintf (stderr, "%s %s %s %s", tagXMLEle(root), findXMLAttValu(root,"device"), findXMLAttValu(root,"name"), findXMLAttValu(root,"state")); pcd = pcdataXMLEle (root); if (pcd[0]) fprintf (stderr, " %s", pcd); perm = findXMLAttValu(root,"perm"); if (perm[0]) fprintf (stderr, " %s", perm); msg = findXMLAttValu(root,"message"); if (msg[0]) fprintf (stderr, " '%s'", msg); /* print each array value */ for (e = nextXMLEle(root,1); e; e = nextXMLEle(root,0)) for (i = 0; i < sizeof(prtags)/sizeof(prtags[0]); i++) if (strcmp (prtags[i], tagXMLEle(e)) == 0) fprintf (stderr, "\n %10s='%s'", findXMLAttValu(e,"name"), pcdataXMLEle (e)); fprintf (stderr, "\n"); }
/* queue the xml command in root from the given device to each * interested client, except notme */ static void send2Clients (ClInfo *notme, XMLEle *root, char *dev) { ClInfo *cp; Msg *mp; /* build a new message */ mp = (Msg *) mymalloc (sizeof(Msg)); mp->ep = root; mp->count = 0; /* lock access to client info */ pthread_mutex_lock (&client_m); /* queue message to each interested client */ for (cp = clinfo; cp < &clinfo[nclinfo]; cp++) { int isblob; /* cp ok? notme? valid dev? blob? */ if (!cp->active || cp == notme) continue; if (findClDevice (cp, dev) < 0) continue; isblob = !strcmp (tagXMLEle(root), "setBLOBVector"); if ((isblob && cp->blob==B_NEVER) || (!isblob && cp->blob==B_ONLY)) continue; /* ok: queue message to given client */ mp->count++; pushFQ (cp->msgq, mp); } /* wake up client write threads, the last of which will free the Msg */ if (mp->count > 0) pthread_cond_broadcast(&client_c); else { if (verbose > 2) fprintf (stderr, "no clients want %s\n", xmlLog(root)); freeMsg (mp); /* no interested clients, free Msg now */ } /* finished with client info */ pthread_mutex_unlock (&client_m); }
/* read more from the given driver, send to each interested client when see * xml closure. if driver dies, try restarting. * return 0 if ok else -1 if had to shut down anything. */ int readFromDriver (DvrInfo *dp) { char buf[MAXRBUF]; int shutany = 0; ssize_t i, nr; /* read driver */ nr = read (dp->rfd, buf, sizeof(buf)); if (nr <= 0) { if (nr < 0) fprintf (stderr, "%s: Driver %s: stdin %s\n", indi_tstamp(NULL), dp->name, strerror(errno)); else fprintf (stderr, "%s: Driver %s: stdin EOF\n", indi_tstamp(NULL), dp->name); shutdownDvr (dp, 1); return (-1); } /* process XML, sending when find closure */ for (i = 0; i < nr; i++) { char err[1024]; XMLEle *root = readXMLEle (dp->lp, buf[i], err); if (root) { char *roottag = tagXMLEle(root); const char *dev = findXMLAttValu (root, "device"); const char *name = findXMLAttValu (root, "name"); int isblob = !strcmp (tagXMLEle(root), "setBLOBVector"); Msg *mp; if (verbose > 2) { fprintf(stderr, "%s: Driver %s: read ", indi_tstamp(0),dp->name); traceMsg (root); } else if (verbose > 1) { fprintf (stderr, "%s: Driver %s: read <%s device='%s' name='%s'>\n", indi_tstamp(NULL), dp->name, tagXMLEle(root), findXMLAttValu (root, "device"), findXMLAttValu (root, "name")); } /* that's all if driver is just registering a snoop */ if (!strcmp (roottag, "getProperties")) { addSDevice (dp, dev, name); delXMLEle (root); continue; } /* that's all if driver is just registering a BLOB mode */ if (!strcmp (roottag, "enableBLOB")) { Property *sp = findSDevice (dp, dev, name); if (sp) crackBLOB (pcdataXMLEle (root), &sp->blob); delXMLEle (root); continue; } /* Found a new device? Let's add it to driver info */ if (dev[0] && isDeviceInDriver(dev, dp) == 0) { dp->dev = (char **) realloc(dp->dev, (dp->ndev+1) * sizeof(char *)); dp->dev[dp->ndev] = (char *) malloc(MAXINDIDEVICE * sizeof(char)); strncpy (dp->dev[dp->ndev], dev, MAXINDIDEVICE-1); dp->dev[dp->ndev][MAXINDIDEVICE-1] = '\0'; #ifdef OSX_EMBEDED_MODE if (!dp->ndev) fprintf(stderr, "STARTED \"%s\"\n", dp->name); fflush(stderr); #endif dp->ndev++; } /* log messages if any and wanted */ if (ldir) logDMsg (root, dev); /* build a new message -- set content iff anyone cares */ mp = newMsg(); /* send to interested clients */ if (q2Clients (NULL, isblob, dev, name, mp, root) < 0) shutany++; /* send to snooping drivers */ q2SDrivers (isblob, dev, name, mp, root); /* set message content if anyone cares else forget it */ if (mp->count > 0) setMsgXMLEle (mp, root); else freeMsg (mp); delXMLEle (root); } else if (err[0]) { char *ts = indi_tstamp(NULL); fprintf (stderr, "%s: Driver %s: XML error: %s\n", ts, dp->name, err); fprintf (stderr, "%s: Driver %s: XML read: %.*s\n", ts, dp->name, (int)nr, buf); shutdownDvr (dp, 1); return (-1); } } return (shutany ? -1 : 0); }
/* read more from the given client, send to each appropriate driver when see * xml closure. also send all newXXX() to all other interested clients. * return -1 if had to shut down anything, else 0. */ int readFromClient (ClInfo *cp) { char buf[MAXRBUF]; int shutany = 0; ssize_t i, nr; /* read client */ nr = read (cp->s, buf, sizeof(buf)); if (nr <= 0) { if (nr < 0) fprintf (stderr, "%s: Client %d: read: %s\n", indi_tstamp(NULL), cp->s, strerror(errno)); else if (verbose > 0) fprintf (stderr, "%s: Client %d: read EOF\n", indi_tstamp(NULL), cp->s); shutdownClient (cp); return (-1); } /* process XML, sending when find closure */ for (i = 0; i < nr; i++) { char err[1024]; XMLEle *root = readXMLEle (cp->lp, buf[i], err); if (root) { char *roottag = tagXMLEle(root); const char *dev = findXMLAttValu (root, "device"); const char *name = findXMLAttValu (root, "name"); int isblob = !strcmp (tagXMLEle(root), "setBLOBVector"); Msg *mp; if (verbose > 2) { fprintf (stderr, "%s: Client %d: read ",indi_tstamp(NULL),cp->s); traceMsg (root); } else if (verbose > 1) { fprintf (stderr, "%s: Client %d: read <%s device='%s' name='%s'>\n", indi_tstamp(NULL), cp->s, tagXMLEle(root), findXMLAttValu (root, "device"), findXMLAttValu (root, "name")); } /* snag interested properties. * N.B. don't open to alldevs if seen specific dev already, else * remote client connections start returning too much. */ if (dev[0]) addClDevice (cp, dev, name, isblob); else if (!strcmp (roottag, "getProperties") && !cp->nprops) cp->allprops = 1; /* snag enableBLOB -- send to remote drivers too */ if (!strcmp (roottag, "enableBLOB")) // crackBLOB (pcdataXMLEle(root), &cp->blob); crackBLOBHandling (dev, name, pcdataXMLEle(root), cp); /* build a new message -- set content iff anyone cares */ mp = newMsg(); /* send message to driver(s) responsible for dev */ q2RDrivers (dev, mp, root); /* echo new* commands back to other clients */ if (!strncmp (roottag, "new", 3)) { if (q2Clients (cp, isblob, dev, name, mp, root) < 0) shutany++; } /* set message content if anyone cares else forget it */ if (mp->count > 0) setMsgXMLEle (mp, root); else freeMsg (mp); delXMLEle (root); } else if (err[0]) { char *ts = indi_tstamp(NULL); fprintf (stderr, "%s: Client %d: XML error: %s\n", ts, cp->s, err); fprintf (stderr, "%s: Client %d: XML read: %.*s\n", ts, cp->s, (int)nr, buf); shutdownClient (cp); return (-1); } } return (shutany ? -1 : 0); }
/* put Msg mp on queue of each client interested in dev/name, except notme. * if BLOB always honor current mode. * return -1 if had to shut down any clients, else 0. */ int q2Clients (ClInfo *notme, int isblob, const char *dev, const char *name, Msg *mp, XMLEle *root) { int shutany = 0; ClInfo *cp; int ql,i=0; /* queue message to each interested client */ for (cp = clinfo; cp < &clinfo[nclinfo]; cp++) { /* cp in use? notme? want this dev/name? blob? */ if (!cp->active || cp == notme) continue; if (findClDevice (cp, dev, name) < 0) continue; //if ((isblob && cp->blob==B_NEVER) || (!isblob && cp->blob==B_ONLY)) if (!isblob && cp->blob==B_ONLY) continue; if (isblob) { if (cp->nprops > 0) { Property *pp = NULL; int blob_found=0; for (i = 0; i < cp->nprops; i++) { pp = &cp->props[i]; if (!strcmp (pp->dev, dev) && (!strcmp(pp->name, name))) { blob_found = 1; break; } } if ( (blob_found && pp->blob == B_NEVER) || (blob_found==0 && cp->blob == B_NEVER) ) continue; } else if (cp->blob == B_NEVER) continue; } /* shut down this client if its q is already too large */ ql = msgQSize(cp->msgq); if (ql > maxqsiz) { if (verbose) fprintf (stderr, "%s: Client %d: %d bytes behind, shutting down\n", indi_tstamp(NULL), cp->s, ql); shutdownClient (cp); shutany++; continue; } /* ok: queue message to this client */ mp->count++; pushFQ (cp->msgq, mp); if (verbose > 1) fprintf (stderr, "%s: Client %d: queuing <%s device='%s' name='%s'>\n", indi_tstamp(NULL), cp->s, tagXMLEle(root), findXMLAttValu (root, "device"), findXMLAttValu (root, "name")); } return (shutany ? -1 : 0); }
/* pull apart the name and value from the given message, and set operand value. * ignore any other messages. * return 0 if found a recognized operand else -1 */ static int setOp (XMLEle *root) { char *t = tagXMLEle (root); const char *d = findXMLAttValu (root, "device"); const char *n = findXMLAttValu (root, "name"); int nset = 0; double v; char prop[1024]; XMLEle *ep; /* check values */ if (!strcmp (t,"defNumberVector") || !strcmp (t,"setNumberVector")) { for (ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) { char *et = tagXMLEle(ep); if (!strcmp (et,"defNumber") || !strcmp (et,"oneNumber")) { sprintf (prop, "%s.%s.%s", d, n, findXMLAttValu(ep,"name")); v = atof(pcdataXMLEle(ep)); if (setOperand (prop, v) == 0) { nset++; if (oflag) fprintf (stderr, "%s=%g\n", prop, v); } } } } else if(!strcmp(t,"defSwitchVector") || !strcmp(t,"setSwitchVector")){ for (ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) { char *et = tagXMLEle(ep); if (!strcmp (et,"defSwitch") || !strcmp (et,"oneSwitch")) { sprintf (prop, "%s.%s.%s", d, n, findXMLAttValu(ep,"name")); v = (double)!strcmp(pcdataXMLEle(ep),"On"); if (setOperand (prop, v) == 0) { nset++; if (oflag) fprintf (stderr, "%s=%g\n", prop, v); } } } } else if(!strcmp(t,"defLightVector") || !strcmp(t,"setLightVector")){ for (ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) { char *et = tagXMLEle(ep); if (!strcmp (et,"defLight") || !strcmp (et,"oneLight")) { sprintf (prop, "%s.%s.%s", d, n, findXMLAttValu(ep,"name")); v = (double)pstatestr(pcdataXMLEle(ep)); if (setOperand (prop, v) == 0) { nset++; if (oflag) fprintf (stderr, "%s=%g\n", prop, v); } } } } /* check special elements */ t = (char *) findXMLAttValu (root, "state"); if (t[0]) { sprintf (prop, "%s.%s._STATE", d, n); v = (double)pstatestr(t); if (setOperand (prop, v) == 0) { nset++; if (oflag) fprintf (stderr, "%s=%g\n", prop, v); } } t = (char *) findXMLAttValu (root, "timestamp"); if (t[0]) { sprintf (prop, "%s.%s._TS", d, n); v = (double)timestamp(t); if (setOperand (prop, v) == 0) { nset++; if (oflag) fprintf (stderr, "%s=%g\n", prop, v); } } /* return whether any were set */ return (nset > 0 ? 0 : -1); }
int INDI::BaseClientQt::dispatchCommand(XMLEle * root, char * errmsg) { if (!strcmp (tagXMLEle(root), "message")) return messageCmd(root, errmsg); else if (!strcmp (tagXMLEle(root), "delProperty")) return delPropertyCmd(root, errmsg); // Just ignore any getProperties we might get else if (!strcmp (tagXMLEle(root), "getProperties")) return INDI_PROPERTY_DUPLICATED; /* Get the device, if not available, create it */ INDI::BaseDevice * dp = findDev (root, 1, errmsg); if (dp == NULL) { strcpy(errmsg,"No device available and none was created"); return INDI_DEVICE_NOT_FOUND; } // FIXME REMOVE THIS // Ignore echoed newXXX and getProperties if (strstr(tagXMLEle(root), "new") ||strstr(tagXMLEle(root), "getProperties")) return 0; if ((!strcmp (tagXMLEle(root), "defTextVector")) || (!strcmp (tagXMLEle(root), "defNumberVector")) || (!strcmp (tagXMLEle(root), "defSwitchVector")) || (!strcmp (tagXMLEle(root), "defLightVector")) || (!strcmp (tagXMLEle(root), "defBLOBVector"))) return dp->buildProp(root, errmsg); else if (!strcmp (tagXMLEle(root), "setTextVector") || !strcmp (tagXMLEle(root), "setNumberVector") || !strcmp (tagXMLEle(root), "setSwitchVector") || !strcmp (tagXMLEle(root), "setLightVector") || !strcmp (tagXMLEle(root), "setBLOBVector")) return dp->setValue(root, errmsg); return INDI_DISPATCH_ERROR; }