Instrument Neutral Distributed Interface INDI  1.9.2
indiserver.c
Go to the documentation of this file.
1 /* INDI Server for protocol version 1.7.
2  * Copyright (C) 2007 Elwood C. Downey <ecdowney@clearskyinstitute.com>
3  2013 Jasem Mutlaq <mutlaqja@ikarustech.com>
4  This library is free software; you can redistribute it and/or
5  modify it under the terms of the GNU Lesser General Public
6  License as published by the Free Software Foundation; either
7  version 2.1 of the License, or (at your option) any later version.
8 
9  This library is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  Lesser General Public License for more details.
13 
14  You should have received a copy of the GNU Lesser General Public
15  License along with this library; if not, write to the Free Software
16  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 
18  * argv lists names of Driver programs to run or sockets to connect for Devices.
19  * Drivers are restarted if they exit or connection closes.
20  * Each local Driver's stdin/out are assumed to provide INDI traffic and are
21  * connected here via pipes. Local Drivers' stderr are connected to our
22  * stderr with date stamp and driver name prepended.
23  * We only support Drivers that advertise support for one Device. The problem
24  * with multiple Devices in one Driver is without a way to know what they
25  * _all_ are there is no way to avoid sending all messages to all Drivers.
26  * Outbound messages are limited to Devices and Properties seen inbound.
27  * Messages to Devices on sockets always include Device so the chained
28  * indiserver will only pass back info from that Device.
29  * All newXXX() received from one Client are echoed to all other Clients who
30  * have shown an interest in the same Device and property.
31  *
32  * 2017-01-29 JM: Added option to drop stream blobs if client blob queue is
33  * higher than maxstreamsiz bytes
34  *
35  * Implementation notes:
36  *
37  * We fork each driver and open a server socket listening for INDI clients.
38  * Then forever we listen for new clients and pass traffic between clients and
39  * drivers, subject to optimizations based on sniffing messages for matching
40  * Devices and Properties. Since one message might be destined to more than
41  * one client or device, they are queued and only removed after the last
42  * consumer is finished. XMLEle are converted to linear strings before being
43  * sent to optimize write system calls and avoid blocking to slow clients.
44  * Clients that get more than maxqsiz bytes behind are shut down.
45  */
46 
47 #define _GNU_SOURCE // needed for siginfo_t and sigaction
48 
49 #include "config.h"
50 
51 #include "fq.h"
52 #include "indiapi.h"
53 #include "indidevapi.h"
54 #include "lilxml.h"
55 
56 #include <errno.h>
57 #include <fcntl.h>
58 #include <libgen.h>
59 #include <netdb.h>
60 #include <signal.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <stdarg.h>
64 #include <string.h>
65 #include <time.h>
66 #include <unistd.h>
67 #include <arpa/inet.h>
68 #include <netinet/in.h>
69 #include <sys/time.h>
70 #include <sys/types.h>
71 #include <sys/wait.h>
72 #include <sys/stat.h>
73 #include <sys/socket.h>
74 
75 #define INDIPORT 7624 /* default TCP/IP port to listen */
76 #define REMOTEDVR (-1234) /* invalid PID to flag remote drivers */
77 #define MAXSBUF 512
78 #define MAXRBUF 49152 /* max read buffering here */
79 #define MAXWSIZ 49152 /* max bytes/write */
80 #define SHORTMSGSIZ 2048 /* buf size for most messages */
81 #define DEFMAXQSIZ 128 /* default max q behind, MB */
82 #define DEFMAXSSIZ 5 /* default max stream behind, MB */
83 #define DEFMAXRESTART 10 /* default max restarts */
84 
85 #ifdef OSX_EMBEDED_MODE
86 #define LOGNAME "/Users/%s/Library/Logs/indiserver.log"
87 #define FIFONAME "/tmp/indiserverFIFO"
88 #endif
89 
90 /* associate a usage count with queuded client or device message */
91 typedef struct
92 {
93  int count; /* number of consumers left */
94  unsigned long cl; /* content length */
95  char *cp; /* content: buf or malloced */
96  char buf[SHORTMSGSIZ]; /* local buf for most messages */
97 } Msg;
98 
99 /* device + property name */
100 typedef struct
101 {
102  char dev[MAXINDIDEVICE];
103  char name[MAXINDINAME];
104  BLOBHandling blob; /* when to snoop BLOBs */
105 } Property;
106 
107 /* record of each snooped property
108 typedef struct {
109  Property prop;
110  BLOBHandling blob;
111 } Property;
112 */
113 
114 struct
115 {
116  const char *name; /* Path to FIFO for dynamic startups & shutdowns of drivers */
117  int fd;
118  //FILE *fs;
119 } fifo;
120 
121 /* info for each connected client */
122 typedef struct
123 {
124  int active; /* 1 when this record is in use */
125  Property *props; /* malloced array of props we want */
126  int nprops; /* n entries in props[] */
127  int allprops; /* saw getProperties w/o device */
128  BLOBHandling blob; /* when to send setBLOBs */
129  int s; /* socket for this client */
130  LilXML *lp; /* XML parsing context */
131  FQ *msgq; /* Msg queue */
132  unsigned int nsent; /* bytes of current Msg sent so far */
133 } ClInfo;
134 static ClInfo *clinfo; /* malloced pool of clients */
135 static int nclinfo; /* n total (not active) */
136 
137 /* info for each connected driver */
138 typedef struct
139 {
140  char name[MAXINDINAME]; /* persistent name */
141  char envDev[MAXSBUF];
142  char envConfig[MAXSBUF];
143  char envSkel[MAXSBUF];
144  char envPrefix[MAXSBUF];
145  char host[MAXSBUF];
146  int port;
147  //char dev[MAXINDIDEVICE]; /* device served by this driver */
148  char **dev; /* device served by this driver */
149  int ndev; /* number of devices served by this driver */
150  int active; /* 1 when this record is in use */
151  Property *sprops; /* malloced array of props we snoop */
152  int nsprops; /* n entries in sprops[] */
153  int pid; /* process id or REMOTEDVR if remote */
154  int rfd; /* read pipe fd */
155  int wfd; /* write pipe fd */
156  int efd; /* stderr from driver, if local */
157  int restarts; /* times process has been restarted */
158  LilXML *lp; /* XML parsing context */
159  FQ *msgq; /* Msg queue */
160  unsigned int nsent; /* bytes of current Msg sent so far */
161 } DvrInfo;
162 static DvrInfo *dvrinfo; /* malloced array of drivers */
163 static int ndvrinfo; /* n total */
164 
165 static char *me; /* our name */
166 static int port = INDIPORT; /* public INDI port */
167 static int verbose; /* chattiness */
168 static int lsocket; /* listen socket */
169 static char *ldir; /* where to log driver messages */
170 static int maxqsiz = (DEFMAXQSIZ * 1024 * 1024); /* kill if these bytes behind */
171 static int maxstreamsiz = (DEFMAXSSIZ * 1024 * 1024); /* drop blobs if these bytes behind while streaming*/
172 static int maxrestarts = DEFMAXRESTART;
173 static int terminateddrv = 0;
174 
175 static void logStartup(int ac, char *av[]);
176 static void usage(void);
177 //static void noZombies(void);
178 static void reapZombies(void);
179 static void noSIGPIPE(void);
180 static void indiFIFO(void);
181 static void indiRun(void);
182 static void indiListen(void);
183 static void newFIFO(void);
184 static void newClient(void);
185 static int newClSocket(void);
186 static void shutdownClient(ClInfo *cp);
187 static int readFromClient(ClInfo *cp);
188 static void startDvr(DvrInfo *dp);
189 static void startLocalDvr(DvrInfo *dp);
190 static void startRemoteDvr(DvrInfo *dp);
191 static int openINDIServer(char host[], int indi_port);
192 static void shutdownDvr(DvrInfo *dp, int restart);
193 static int isDeviceInDriver(const char *dev, DvrInfo *dp);
194 static void q2RDrivers(const char *dev, Msg *mp, XMLEle *root);
195 static void q2SDrivers(DvrInfo *me, int isblob, const char *dev, const char *name, Msg *mp, XMLEle *root);
196 static int q2Clients(ClInfo *notme, int isblob, const char *dev, const char *name, Msg *mp, XMLEle *root);
197 static int q2Servers(DvrInfo *me, Msg *mp, XMLEle *root);
198 static void addSDevice(DvrInfo *dp, const char *dev, const char *name);
199 static Property *findSDevice(DvrInfo *dp, const char *dev, const char *name);
200 static void addClDevice(ClInfo *cp, const char *dev, const char *name, int isblob);
201 static int findClDevice(ClInfo *cp, const char *dev, const char *name);
202 static int readFromDriver(DvrInfo *dp);
203 static int stderrFromDriver(DvrInfo *dp);
204 static int msgQSize(FQ *q);
205 static void setMsgXMLEle(Msg *mp, XMLEle *root);
206 static void setMsgStr(Msg *mp, char *str);
207 static void freeMsg(Msg *mp);
208 static Msg *newMsg(void);
209 static int sendClientMsg(ClInfo *cp);
210 static int sendDriverMsg(DvrInfo *cp);
211 static void crackBLOB(const char *enableBLOB, BLOBHandling *bp);
212 static void crackBLOBHandling(const char *dev, const char *name, const char *enableBLOB, ClInfo *cp);
213 static void traceMsg(XMLEle *root);
214 static char *indi_tstamp(char *s);
215 static void logDMsg(XMLEle *root, const char *dev);
216 static void Bye(void);
217 
218 int main(int ac, char *av[])
219 {
220  /* log startup */
221  logStartup(ac, av);
222 
223  /* save our name */
224  me = av[0];
225 
226 #ifdef OSX_EMBEDED_MODE
227 
228  char logname[128];
229  snprintf(logname, 128, LOGNAME, getlogin());
230  fprintf(stderr, "switching stderr to %s", logname);
231  freopen(logname, "w", stderr);
232 
233  fifo.name = FIFONAME;
234  verbose = 1;
235  ac = 0;
236 
237 #else
238 
239  /* crack args */
240  while ((--ac > 0) && ((*++av)[0] == '-'))
241  {
242  char *s;
243  for (s = av[0] + 1; *s != '\0'; s++)
244  switch (*s)
245  {
246  case 'l':
247  if (ac < 2)
248  {
249  fprintf(stderr, "-l requires log directory\n");
250  usage();
251  }
252  ldir = *++av;
253  ac--;
254  break;
255  case 'm':
256  if (ac < 2)
257  {
258  fprintf(stderr, "-m requires max MB behind\n");
259  usage();
260  }
261  maxqsiz = 1024 * 1024 * atoi(*++av);
262  ac--;
263  break;
264  case 'p':
265  if (ac < 2)
266  {
267  fprintf(stderr, "-p requires port value\n");
268  usage();
269  }
270  port = atoi(*++av);
271  ac--;
272  break;
273  case 'd':
274  if (ac < 2)
275  {
276  fprintf(stderr, "-d requires max stream MB behind\n");
277  usage();
278  }
279  maxstreamsiz = 1024 * 1024 * atoi(*++av);
280  ac--;
281  break;
282  case 'f':
283  if (ac < 2)
284  {
285  fprintf(stderr, "-f requires fifo node\n");
286  usage();
287  }
288  fifo.name = *++av;
289  ac--;
290  break;
291  case 'r':
292  if (ac < 2)
293  {
294  fprintf(stderr, "-r requires number of restarts\n");
295  usage();
296  }
297  maxrestarts = atoi(*++av);
298  if (maxrestarts < 0)
299  maxrestarts = 0;
300  ac--;
301  break;
302  case 'v':
303  verbose++;
304  break;
305  default:
306  usage();
307  }
308  }
309 #endif
310 
311  /* at this point there are ac args in av[] to name our drivers */
312  if (ac == 0 && !fifo.name)
313  usage();
314 
315  /* take care of some unixisms */
316  /*noZombies();*/
317  reapZombies();
318  noSIGPIPE();
319 
320  /* realloc seed for client pool */
321  clinfo = (ClInfo *)malloc(1);
322  nclinfo = 0;
323 
324  /* create driver info array all at once since size never changes */
325  ndvrinfo = ac;
326  dvrinfo = (DvrInfo *)calloc(ndvrinfo, sizeof(DvrInfo));
327 
328  /* start each driver */
329  while (ac-- > 0)
330  {
331  strncpy(dvrinfo[ac].name, *av++, MAXINDINAME);
332  startDvr(&dvrinfo[ac]);
333  }
334 
335  /* announce we are online */
336  indiListen();
337 
338  /* Load up FIFO, if available */
339  indiFIFO();
340 
341  /* handle new clients and all io */
342  while (1)
343  indiRun();
344 
345  /* whoa! */
346  fprintf(stderr, "unexpected return from main\n");
347  return (1);
348 }
349 
350 /* record we have started and our args */
351 static void logStartup(int ac, char *av[])
352 {
353  int i;
354 
355  fprintf(stderr, "%s: startup: ", indi_tstamp(NULL));
356  for (i = 0; i < ac; i++)
357  fprintf(stderr, "%s ", av[i]);
358  fprintf(stderr, "\n");
359 }
360 
361 /* print usage message and exit (2) */
362 static void usage(void)
363 {
364  fprintf(stderr, "Usage: %s [options] driver [driver ...]\n", me);
365  fprintf(stderr, "Purpose: server for local and remote INDI drivers\n");
366  fprintf(stderr, "INDI Library: %s\nCode %s. Protocol %g.\n", CMAKE_INDI_VERSION_STRING, GIT_TAG_STRING, INDIV);
367  fprintf(stderr, "Options:\n");
368  fprintf(stderr, " -l d : log driver messages to <d>/YYYY-MM-DD.islog\n");
369  fprintf(stderr, " -m m : kill client if gets more than this many MB behind, default %d\n", DEFMAXQSIZ);
370  fprintf(stderr,
371  " -d m : drop streaming blobs if client gets more than this many MB behind, default %d. 0 to disable\n",
372  DEFMAXSSIZ);
373  fprintf(stderr, " -p p : alternate IP port, default %d\n", INDIPORT);
374  fprintf(stderr, " -r r : maximum driver restarts on error, default %d\n", DEFMAXRESTART);
375  fprintf(stderr, " -f path : Path to fifo for dynamic startup and shutdown of drivers.\n");
376  fprintf(stderr, " -v : show key events, no traffic\n");
377  fprintf(stderr, " -vv : -v + key message content\n");
378  fprintf(stderr, " -vvv : -vv + complete xml\n");
379  fprintf(stderr, "driver : executable or [device]@host[:port]\n");
380 
381  exit(2);
382 }
383 
384 /* arrange for no zombies if drivers die */
385 //static void noZombies()
386 //{
387 // struct sigaction sa;
388 // sa.sa_handler = SIG_IGN;
389 // sigemptyset(&sa.sa_mask);
390 //#ifdef SA_NOCLDWAIT
391 // sa.sa_flags = SA_NOCLDWAIT;
392 //#else
393 // sa.sa_flags = 0;
394 //#endif
395 // (void)sigaction(SIGCHLD, &sa, NULL);
396 //}
397 
398 /* reap zombies when drivers die, in order to leave SIGCHLD unmodified for subprocesses */
399 static void zombieRaised(int signum, siginfo_t *sig, void *data)
400 {
401  INDI_UNUSED(data);
402  switch (signum)
403  {
404  case SIGCHLD:
405  fprintf(stderr, "Child process %d died\n", sig->si_pid);
406  waitpid(sig->si_pid, NULL, WNOHANG);
407  break;
408 
409  default:
410  fprintf(stderr, "Received unexpected signal %d\n", signum);
411  }
412 }
413 
414 /* reap zombies as they die */
415 static void reapZombies()
416 {
417  struct sigaction sa;
418  sa.sa_sigaction = zombieRaised;
419  sigemptyset(&sa.sa_mask);
420  sa.sa_flags = SA_SIGINFO;
421  (void)sigaction(SIGCHLD, &sa, NULL);
422 }
423 
424 /* turn off SIGPIPE on bad write so we can handle it inline */
425 static void noSIGPIPE()
426 {
427  struct sigaction sa;
428  sa.sa_handler = SIG_IGN;
429  sigemptyset(&sa.sa_mask);
430  (void)sigaction(SIGPIPE, &sa, NULL);
431 }
432 
433 static DvrInfo *allocDvr()
434 {
435  DvrInfo *dp = NULL;
436  int dvi;
437 
438  /* try to reuse a driver slot, else add one */
439  for (dvi = 0; dvi < ndvrinfo; dvi++)
440  if (!(dp = &dvrinfo[dvi])->active)
441  break;
442  if (dvi == ndvrinfo)
443  {
444  /* grow dvrinfo */
445  dvrinfo = (DvrInfo *)realloc(dvrinfo, (ndvrinfo + 1) * sizeof(DvrInfo));
446  if (!dvrinfo)
447  {
448  fprintf(stderr, "no memory for new drivers\n");
449  Bye();
450  }
451  dp = &dvrinfo[ndvrinfo++];
452  }
453 
454  if (dp == NULL)
455  return NULL;
456 
457  /* rig up new dvrinfo entry */
458  memset(dp, 0, sizeof(*dp));
459  dp->active = 1;
460  dp->ndev = 0;
461 
462  return dp;
463 }
464 
465 /* start the given INDI driver process or connection.
466  * exit if trouble.
467  */
468 static void startDvr(DvrInfo *dp)
469 {
470  if (strchr(dp->name, '@'))
471  startRemoteDvr(dp);
472  else
473  startLocalDvr(dp);
474 }
475 
476 /* start the given local INDI driver process.
477  * exit if trouble.
478  */
479 static void startLocalDvr(DvrInfo *dp)
480 {
481  Msg *mp;
482  char buf[32];
483  int rp[2], wp[2], ep[2];
484  int pid;
485 
486 #ifdef OSX_EMBEDED_MODE
487  fprintf(stderr, "STARTING \"%s\"\n", dp->name);
488  fflush(stderr);
489 #endif
490 
491  /* build three pipes: r, w and error*/
492  if (pipe(rp) < 0)
493  {
494  fprintf(stderr, "%s: read pipe: %s\n", indi_tstamp(NULL), strerror(errno));
495  Bye();
496  }
497  if (pipe(wp) < 0)
498  {
499  fprintf(stderr, "%s: write pipe: %s\n", indi_tstamp(NULL), strerror(errno));
500  Bye();
501  }
502  if (pipe(ep) < 0)
503  {
504  fprintf(stderr, "%s: stderr pipe: %s\n", indi_tstamp(NULL), strerror(errno));
505  Bye();
506  }
507 
508  /* fork&exec new process */
509  pid = fork();
510  if (pid < 0)
511  {
512  fprintf(stderr, "%s: fork: %s\n", indi_tstamp(NULL), strerror(errno));
513  Bye();
514  }
515  if (pid == 0)
516  {
517  /* child: exec name */
518  int fd;
519 
520  /* rig up pipes */
521  dup2(wp[0], 0); /* driver stdin reads from wp[0] */
522  dup2(rp[1], 1); /* driver stdout writes to rp[1] */
523  dup2(ep[1], 2); /* driver stderr writes to e[]1] */
524  for (fd = 3; fd < 100; fd++)
525  (void)close(fd);
526 
527  if (*dp->envDev)
528  setenv("INDIDEV", dp->envDev, 1);
529  /* Only reset environment variable in case of FIFO */
530  else if (fifo.fd > 0)
531  unsetenv("INDIDEV");
532  if (*dp->envConfig)
533  setenv("INDICONFIG", dp->envConfig, 1);
534  else if (fifo.fd > 0)
535  unsetenv("INDICONFIG");
536  if (*dp->envSkel)
537  setenv("INDISKEL", dp->envSkel, 1);
538  else if (fifo.fd > 0)
539  unsetenv("INDISKEL");
540  char executable[MAXSBUF];
541  if (*dp->envPrefix)
542  {
543  setenv("INDIPREFIX", dp->envPrefix, 1);
544 #if defined(OSX_EMBEDED_MODE)
545  snprintf(executable, MAXSBUF, "%s/Contents/MacOS/%s", dp->envPrefix, dp->name);
546 #elif defined(__APPLE__)
547  snprintf(executable, MAXSBUF, "%s/%s", dp->envPrefix, dp->name);
548 #else
549  snprintf(executable, MAXSBUF, "%s/bin/%s", dp->envPrefix, dp->name);
550 #endif
551 
552  fprintf(stderr, "%s\n", executable);
553 
554  execlp(executable, dp->name, NULL);
555  }
556  else
557  {
558  if (fifo.fd > 0)
559  unsetenv("INDIPREFIX");
560  if (dp->name[0] == '.')
561  {
562  snprintf(executable, MAXSBUF, "%s/%s", dirname(me), dp->name);
563  execlp(executable, dp->name, NULL);
564  }
565  else
566  {
567  execlp(dp->name, dp->name, NULL);
568  }
569  }
570 
571 #ifdef OSX_EMBEDED_MODE
572  fprintf(stderr, "FAILED \"%s\"\n", dp->name);
573  fflush(stderr);
574 #endif
575  fprintf(stderr, "%s: Driver %s: execlp: %s\n", indi_tstamp(NULL), dp->name, strerror(errno));
576  _exit(1); /* parent will notice EOF shortly */
577  }
578 
579  /* don't need child's side of pipes */
580  close(wp[0]);
581  close(rp[1]);
582  close(ep[1]);
583 
584  /* record pid, io channels, init lp and snoop list */
585  dp->pid = pid;
586  strncpy(dp->host, "localhost", MAXSBUF);
587  dp->port = -1;
588  dp->rfd = rp[0];
589  dp->wfd = wp[1];
590  dp->efd = ep[0];
591  dp->lp = newLilXML();
592  dp->msgq = newFQ(1);
593  dp->sprops = (Property *)malloc(1); /* seed for realloc */
594  dp->nsprops = 0;
595  dp->nsent = 0;
596  dp->active = 1;
597  dp->ndev = 0;
598  dp->dev = (char **)malloc(sizeof(char *));
599 
600  /* first message primes driver to report its properties -- dev known
601  * if restarting
602  */
603  mp = newMsg();
604  pushFQ(dp->msgq, mp);
605  snprintf(buf, sizeof(buf), "<getProperties version='%g'/>\n", INDIV);
606  setMsgStr(mp, buf);
607  mp->count++;
608 
609  if (verbose > 0)
610  fprintf(stderr, "%s: Driver %s: pid=%d rfd=%d wfd=%d efd=%d\n", indi_tstamp(NULL), dp->name, dp->pid, dp->rfd,
611  dp->wfd, dp->efd);
612 }
613 
614 /* start the given remote INDI driver connection.
615  * exit if trouble.
616  */
617 static void startRemoteDvr(DvrInfo *dp)
618 {
619  Msg *mp;
620  char dev[MAXINDIDEVICE] = {0};
621  char host[MAXSBUF] = {0};
622  char buf[MAXSBUF] = {0};
623  int indi_port, sockfd;
624 
625  /* extract host and port */
626  indi_port = INDIPORT;
627  if (sscanf(dp->name, "%[^@]@%[^:]:%d", dev, host, &indi_port) < 2)
628  {
629  // Device missing? Try a different syntax for all devices
630  if (sscanf(dp->name, "@%[^:]:%d", host, &indi_port) < 1)
631  {
632  fprintf(stderr, "Bad remote device syntax: %s\n", dp->name);
633  Bye();
634  }
635  }
636 
637  /* connect */
638  sockfd = openINDIServer(host, indi_port);
639 
640  /* record flag pid, io channels, init lp and snoop list */
641  dp->pid = REMOTEDVR;
642  strncpy(dp->host, host, MAXSBUF);
643  dp->port = indi_port;
644  dp->rfd = sockfd;
645  dp->wfd = sockfd;
646  dp->lp = newLilXML();
647  dp->msgq = newFQ(1);
648  dp->sprops = (Property *)malloc(1); /* seed for realloc */
649  dp->nsprops = 0;
650  dp->nsent = 0;
651  dp->active = 1;
652  dp->ndev = 1;
653  dp->dev = (char **)malloc(sizeof(char *));
654 
655  /* N.B. storing name now is key to limiting outbound traffic to this
656  * dev.
657  */
658  dp->dev[0] = (char *)malloc(MAXINDIDEVICE * sizeof(char));
659  strncpy(dp->dev[0], dev, MAXINDIDEVICE - 1);
660  dp->dev[0][MAXINDIDEVICE - 1] = '\0';
661 
662  /* Sending getProperties with device lets remote server limit its
663  * outbound (and our inbound) traffic on this socket to this device.
664  */
665  mp = newMsg();
666  pushFQ(dp->msgq, mp);
667  if (dev[0])
668  sprintf(buf, "<getProperties device='%s' version='%g'/>\n", dp->dev[0], INDIV);
669  else
670  // This informs downstream server that it is connecting to an upstream server
671  // and not a regular client. The difference is in how it treats snooping properties
672  // among properties.
673  sprintf(buf, "<getProperties device='*' version='%g'/>\n", INDIV);
674  setMsgStr(mp, buf);
675  mp->count++;
676 
677  if (verbose > 0)
678  fprintf(stderr, "%s: Driver %s: socket=%d\n", indi_tstamp(NULL), dp->name, sockfd);
679 }
680 
681 /* open a connection to the given host and port or die.
682  * return socket fd.
683  */
684 static int openINDIServer(char host[], int indi_port)
685 {
686  struct sockaddr_in serv_addr;
687  struct hostent *hp;
688  int sockfd;
689 
690  /* lookup host address */
691  hp = gethostbyname(host);
692  if (!hp)
693  {
694  fprintf(stderr, "gethostbyname(%s): %s\n", host, strerror(errno));
695  Bye();
696  }
697 
698  /* create a socket to the INDI server */
699  (void)memset((char *)&serv_addr, 0, sizeof(serv_addr));
700  serv_addr.sin_family = AF_INET;
701  serv_addr.sin_addr.s_addr = ((struct in_addr *)(hp->h_addr_list[0]))->s_addr;
702  serv_addr.sin_port = htons(indi_port);
703  if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
704  {
705  fprintf(stderr, "socket(%s,%d): %s\n", host, indi_port, strerror(errno));
706  Bye();
707  }
708 
709  /* connect */
710  if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
711  {
712  fprintf(stderr, "connect(%s,%d): %s\n", host, indi_port, strerror(errno));
713  Bye();
714  }
715 
716  /* ok */
717  return (sockfd);
718 }
719 
720 /* create the public INDI Driver endpoint lsocket on port.
721  * return server socket else exit.
722  */
723 static void indiListen()
724 {
725  struct sockaddr_in serv_socket;
726  int sfd;
727  int reuse = 1;
728 
729  /* make socket endpoint */
730  if ((sfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
731  {
732  fprintf(stderr, "%s: socket: %s\n", indi_tstamp(NULL), strerror(errno));
733  Bye();
734  }
735 
736  /* bind to given port for any IP address */
737  memset(&serv_socket, 0, sizeof(serv_socket));
738  serv_socket.sin_family = AF_INET;
739 #ifdef SSH_TUNNEL
740  serv_socket.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
741 #else
742  serv_socket.sin_addr.s_addr = htonl(INADDR_ANY);
743 #endif
744  serv_socket.sin_port = htons((unsigned short)port);
745  if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
746  {
747  fprintf(stderr, "%s: setsockopt: %s\n", indi_tstamp(NULL), strerror(errno));
748  Bye();
749  }
750  if (bind(sfd, (struct sockaddr *)&serv_socket, sizeof(serv_socket)) < 0)
751  {
752  fprintf(stderr, "%s: bind: %s\n", indi_tstamp(NULL), strerror(errno));
753  Bye();
754  }
755 
756  /* willing to accept connections with a backlog of 5 pending */
757  if (listen(sfd, 5) < 0)
758  {
759  fprintf(stderr, "%s: listen: %s\n", indi_tstamp(NULL), strerror(errno));
760  Bye();
761  }
762 
763  /* ok */
764  lsocket = sfd;
765  if (verbose > 0)
766  fprintf(stderr, "%s: listening to port %d on fd %d\n", indi_tstamp(NULL), port, sfd);
767 }
768 
769 /* Attempt to open up FIFO */
770 static void indiFIFO(void)
771 {
772  close(fifo.fd);
773  fifo.fd = -1;
774 
775  /* Open up FIFO, if available */
776  if (fifo.name)
777  {
778  fifo.fd = open(fifo.name, O_RDWR | O_NONBLOCK);
779 
780  if (fifo.fd < 0)
781  {
782  fprintf(stderr, "%s: open(%s): %s.\n", indi_tstamp(NULL), fifo.name, strerror(errno));
783  Bye();
784  }
785  }
786 }
787 
788 /* service traffic from clients and drivers */
789 static void indiRun(void)
790 {
791  fd_set rs, ws;
792  int maxfd = 0;
793  int i, s;
794 
795  /* init with no writers or readers */
796  FD_ZERO(&ws);
797  FD_ZERO(&rs);
798 
799  if (fifo.name && fifo.fd >= 0)
800  {
801  FD_SET(fifo.fd, &rs);
802  maxfd = fifo.fd;
803  }
804 
805  /* always listen for new clients */
806  FD_SET(lsocket, &rs);
807  if (lsocket > maxfd)
808  maxfd = lsocket;
809 
810  /* add all client readers and client writers with work to send */
811  for (i = 0; i < nclinfo; i++)
812  {
813  ClInfo *cp = &clinfo[i];
814  if (cp->active)
815  {
816  FD_SET(cp->s, &rs);
817  if (nFQ(cp->msgq) > 0)
818  FD_SET(cp->s, &ws);
819  if (cp->s > maxfd)
820  maxfd = cp->s;
821  }
822  }
823 
824  /* add all driver readers and driver writers with work to send */
825  for (i = 0; i < ndvrinfo; i++)
826  {
827  DvrInfo *dp = &dvrinfo[i];
828  if (dp->active)
829  {
830  FD_SET(dp->rfd, &rs);
831  if (dp->rfd > maxfd)
832  maxfd = dp->rfd;
833  if (dp->pid != REMOTEDVR)
834  {
835  FD_SET(dp->efd, &rs);
836  if (dp->efd > maxfd)
837  maxfd = dp->efd;
838  }
839  if (nFQ(dp->msgq) > 0)
840  {
841  FD_SET(dp->wfd, &ws);
842  if (dp->wfd > maxfd)
843  maxfd = dp->wfd;
844  }
845  }
846  }
847 
848  /* wait for action */
849  s = select(maxfd + 1, &rs, &ws, NULL, NULL);
850  if (s < 0)
851  {
852  if(errno == EINTR)
853  return;
854  fprintf(stderr, "%s: select(%d): %s\n", indi_tstamp(NULL), maxfd + 1, strerror(errno));
855  Bye();
856  }
857 
858  /* new command from FIFO? */
859  if (s > 0 && fifo.fd >= 0 && FD_ISSET(fifo.fd, &rs))
860  {
861  newFIFO();
862  s--;
863  }
864 
865  /* new client? */
866  if (s > 0 && FD_ISSET(lsocket, &rs))
867  {
868  newClient();
869  s--;
870  }
871 
872  /* message to/from client? */
873  for (i = 0; s > 0 && i < nclinfo; i++)
874  {
875  ClInfo *cp = &clinfo[i];
876  if (cp->active)
877  {
878  if (FD_ISSET(cp->s, &rs))
879  {
880  if (readFromClient(cp) < 0)
881  return; /* fds effected */
882  s--;
883  }
884  if (s > 0 && FD_ISSET(cp->s, &ws))
885  {
886  if (sendClientMsg(cp) < 0)
887  return; /* fds effected */
888  s--;
889  }
890  }
891  }
892 
893  /* message to/from driver? */
894  for (i = 0; s > 0 && i < ndvrinfo; i++)
895  {
896  DvrInfo *dp = &dvrinfo[i];
897  if (dp->active)
898  {
899  if (dp->pid != REMOTEDVR && FD_ISSET(dp->efd, &rs))
900  {
901  if (stderrFromDriver(dp) < 0)
902  return; /* fds effected */
903  s--;
904  }
905  if (s > 0 && FD_ISSET(dp->rfd, &rs))
906  {
907  if (readFromDriver(dp) < 0)
908  return; /* fds effected */
909  s--;
910  }
911  if (s > 0 && FD_ISSET(dp->wfd, &ws) && nFQ(dp->msgq) > 0)
912  {
913  if (sendDriverMsg(dp) < 0)
914  return; /* fds effected */
915  s--;
916  }
917  }
918  }
919 }
920 
921 int isDeviceInDriver(const char *dev, DvrInfo *dp)
922 {
923  int i = 0;
924  for (i = 0; i < dp->ndev; i++)
925  {
926  if (!strcmp(dev, dp->dev[i]))
927  return 1;
928  }
929 
930  return 0;
931 }
932 
933 /* Read commands from FIFO and process them. Start/stop drivers accordingly */
934 static void newFIFO(void)
935 {
936  //char line[MAXRBUF], tDriver[MAXRBUF], tConfig[MAXRBUF], tDev[MAXRBUF], tSkel[MAXRBUF], envDev[MAXRBUF], envConfig[MAXRBUF], envSkel[MAXR];
937  char line[MAXRBUF];
938  DvrInfo *dp = NULL;
939  int startCmd = 0, i = 0, remoteDriver = 0;
940 
941  while (i < MAXRBUF)
942  {
943  if (read(fifo.fd, line + i, 1) <= 0)
944  {
945  // Reset FIFO now, otherwise select will always return with no data from FIFO.
946  indiFIFO();
947  return;
948  }
949 
950  if (line[i] == '\n')
951  {
952  line[i] = '\0';
953  i = 0;
954  }
955  else
956  {
957  i++;
958  continue;
959  }
960 
961  if (verbose)
962  fprintf(stderr, "FIFO: %s\n", line);
963 
964  char cmd[MAXSBUF], arg[4][1], var[4][MAXSBUF], tDriver[MAXSBUF], tName[MAXSBUF], envConfig[MAXSBUF],
965  envSkel[MAXSBUF], envPrefix[MAXSBUF];
966 
967  memset(&tDriver[0], 0, sizeof(char) * MAXSBUF);
968  memset(&tName[0], 0, sizeof(char) * MAXSBUF);
969  memset(&envConfig[0], 0, sizeof(char) * MAXSBUF);
970  memset(&envSkel[0], 0, sizeof(char) * MAXSBUF);
971  memset(&envPrefix[0], 0, sizeof(char) * MAXSBUF);
972 
973  int n = 0;
974 
975  // If remote driver
976  if (strstr(line, "@"))
977  {
978  n = sscanf(line, "%s %512[^\n]", cmd, tDriver);
979 
980  // Remove quotes if any
981  char *ptr = tDriver;
982  int len = strlen(tDriver);
983  while ((ptr = strstr(tDriver, "\"")))
984  {
985  memmove(ptr, ptr + 1, --len);
986  ptr[len] = '\0';
987  }
988 
989  //fprintf(stderr, "Remote Driver: %s\n", tDriver);
990  remoteDriver = 1;
991  }
992  // If local driver
993  else
994  {
995  n = sscanf(line, "%s %s -%1c \"%512[^\"]\" -%1c \"%512[^\"]\" -%1c \"%512[^\"]\" -%1c \"%512[^\"]\"", cmd,
996  tDriver, arg[0], var[0], arg[1], var[1], arg[2], var[2], arg[3], var[3]);
997  remoteDriver = 0;
998  }
999 
1000  int n_args = (n - 2) / 2;
1001 
1002  int j = 0;
1003  for (j = 0; j < n_args; j++)
1004  {
1005  //fprintf(stderr, "arg[%d]: %c\n", i, arg[j][0]);
1006  //fprintf(stderr, "var[%d]: %s\n", i, var[j]);
1007 
1008  if (arg[j][0] == 'n')
1009  {
1010  strncpy(tName, var[j], MAXSBUF - 1);
1011  tName[MAXSBUF - 1] = '\0';
1012 
1013  if (verbose)
1014  fprintf(stderr, "With name: %s\n", tName);
1015  }
1016  else if (arg[j][0] == 'c')
1017  {
1018  strncpy(envConfig, var[j], MAXSBUF - 1);
1019  envConfig[MAXSBUF - 1] = '\0';
1020 
1021  if (verbose)
1022  fprintf(stderr, "With config: %s\n", envConfig);
1023  }
1024  else if (arg[j][0] == 's')
1025  {
1026  strncpy(envSkel, var[j], MAXSBUF - 1);
1027  envSkel[MAXSBUF - 1] = '\0';
1028 
1029  if (verbose)
1030  fprintf(stderr, "With skeketon: %s\n", envSkel);
1031  }
1032  else if (arg[j][0] == 'p')
1033  {
1034  strncpy(envPrefix, var[j], MAXSBUF - 1);
1035  envPrefix[MAXSBUF - 1] = '\0';
1036 
1037  if (verbose)
1038  fprintf(stderr, "With prefix: %s\n", envPrefix);
1039  }
1040  }
1041 
1042  if (!strcmp(cmd, "start"))
1043  startCmd = 1;
1044  else
1045  startCmd = 0;
1046 
1047  if (startCmd)
1048  {
1049  if (verbose)
1050  fprintf(stderr, "FIFO: Starting driver %s\n", tDriver);
1051  dp = allocDvr();
1052  strncpy(dp->name, tDriver, MAXINDIDEVICE);
1053 
1054  if (remoteDriver == 0)
1055  {
1056  //strncpy(dp->dev, tName, MAXINDIDEVICE);
1057  strncpy(dp->envDev, tName, MAXSBUF);
1058  strncpy(dp->envConfig, envConfig, MAXSBUF);
1059  strncpy(dp->envSkel, envSkel, MAXSBUF);
1060  strncpy(dp->envPrefix, envPrefix, MAXSBUF);
1061  startDvr(dp);
1062  }
1063  else
1064  startRemoteDvr(dp);
1065  }
1066  else
1067  {
1068  for (dp = dvrinfo; dp < &dvrinfo[ndvrinfo]; dp++)
1069  {
1070  fprintf(stderr, "dp->name: %s - tDriver: %s\n", dp->name, tDriver);
1071  if (!strcmp(dp->name, tDriver) && dp->active == 1)
1072  {
1073  fprintf(stderr, "name: %s - dp->dev[0]: %s\n", tName, dp->dev[0]);
1074 
1075  /* If device name is given, check against it before shutting down */
1076  //if (tName[0] && strcmp(dp->dev[0], tName))
1077  if (tName[0] && isDeviceInDriver(tName, dp) == 0)
1078  continue;
1079  if (verbose)
1080  fprintf(stderr, "FIFO: Shutting down driver: %s\n", tDriver);
1081 
1082  // for (i = 0; i < dp->ndev; i++)
1083  // {
1084  // /* Inform clients that this driver is dead */
1085  // XMLEle *root = addXMLEle(NULL, "delProperty");
1086  // addXMLAtt(root, "device", dp->dev[i]);
1087 
1088  // prXMLEle(stderr, root, 0);
1089  // Msg *mp = newMsg();
1090 
1091  // q2Clients(NULL, 0, dp->dev[i], NULL, mp, root);
1092  // if (mp->count > 0)
1093  // setMsgXMLEle(mp, root);
1094  // else
1095  // freeMsg(mp);
1096  // delXMLEle(root);
1097  // }
1098 
1099  shutdownDvr(dp, 0);
1100  break;
1101  }
1102  }
1103  }
1104  }
1105 }
1106 
1107 /* prepare for new client arriving on lsocket.
1108  * exit if trouble.
1109  */
1110 static void newClient()
1111 {
1112  ClInfo *cp = NULL;
1113  int s, cli;
1114 
1115  /* assign new socket */
1116  s = newClSocket();
1117 
1118  /* try to reuse a clinfo slot, else add one */
1119  for (cli = 0; cli < nclinfo; cli++)
1120  if (!(cp = &clinfo[cli])->active)
1121  break;
1122  if (cli == nclinfo)
1123  {
1124  /* grow clinfo */
1125  clinfo = (ClInfo *)realloc(clinfo, (nclinfo + 1) * sizeof(ClInfo));
1126  if (!clinfo)
1127  {
1128  fprintf(stderr, "no memory for new client\n");
1129  Bye();
1130  }
1131  cp = &clinfo[nclinfo++];
1132  }
1133 
1134  if (cp == NULL)
1135  return;
1136 
1137  /* rig up new clinfo entry */
1138  memset(cp, 0, sizeof(*cp));
1139  cp->active = 1;
1140  cp->s = s;
1141  cp->lp = newLilXML();
1142  cp->msgq = newFQ(1);
1143  cp->props = malloc(1);
1144  cp->nsent = 0;
1145 
1146  if (verbose > 0)
1147  {
1148  struct sockaddr_in addr;
1149  socklen_t len = sizeof(addr);
1150  getpeername(s, (struct sockaddr *)&addr, &len);
1151  fprintf(stderr, "%s: Client %d: new arrival from %s:%d - welcome!\n", indi_tstamp(NULL), cp->s,
1152  inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
1153  }
1154 #ifdef OSX_EMBEDED_MODE
1155  int active = 0;
1156  for (int i = 0; i < nclinfo; i++)
1157  if (clinfo[i].active)
1158  active++;
1159  fprintf(stderr, "CLIENTS %d\n", active);
1160  fflush(stderr);
1161 #endif
1162 }
1163 
1164 /* read more from the given client, send to each appropriate driver when see
1165  * xml closure. also send all newXXX() to all other interested clients.
1166  * return -1 if had to shut down anything, else 0.
1167  */
1168 static int readFromClient(ClInfo *cp)
1169 {
1170  char buf[MAXRBUF];
1171  int shutany = 0;
1172  ssize_t i, nr;
1173 
1174  /* read client */
1175  nr = read(cp->s, buf, sizeof(buf));
1176  if (nr <= 0)
1177  {
1178  if (nr < 0)
1179  fprintf(stderr, "%s: Client %d: read: %s\n", indi_tstamp(NULL), cp->s, strerror(errno));
1180  else if (verbose > 0)
1181  fprintf(stderr, "%s: Client %d: read EOF\n", indi_tstamp(NULL), cp->s);
1182  shutdownClient(cp);
1183  return (-1);
1184  }
1185 
1186  /* process XML, sending when find closure */
1187  for (i = 0; i < nr; i++)
1188  {
1189  char err[1024];
1190  XMLEle *root = readXMLEle(cp->lp, buf[i], err);
1191  if (root)
1192  {
1193  char *roottag = tagXMLEle(root);
1194  const char *dev = findXMLAttValu(root, "device");
1195  const char *name = findXMLAttValu(root, "name");
1196  int isblob = !strcmp(tagXMLEle(root), "setBLOBVector");
1197  Msg *mp;
1198 
1199  if (verbose > 2)
1200  {
1201  fprintf(stderr, "%s: Client %d: read ", indi_tstamp(NULL), cp->s);
1202  traceMsg(root);
1203  }
1204  else if (verbose > 1)
1205  {
1206  fprintf(stderr, "%s: Client %d: read <%s device='%s' name='%s'>\n", indi_tstamp(NULL), cp->s,
1207  tagXMLEle(root), findXMLAttValu(root, "device"), findXMLAttValu(root, "name"));
1208  }
1209 
1210  /* snag interested properties.
1211  * N.B. don't open to alldevs if seen specific dev already, else
1212  * remote client connections start returning too much.
1213  */
1214  if (dev[0])
1215  {
1216  // Signature for CHAINED SERVER
1217  // Not a regular client.
1218  if (dev[0] == '*' && !cp->nprops)
1219  cp->allprops = 2;
1220  else
1221  addClDevice(cp, dev, name, isblob);
1222  }
1223  else if (!strcmp(roottag, "getProperties") && !cp->nprops && cp->allprops != 2)
1224  cp->allprops = 1;
1225 
1226  /* snag enableBLOB -- send to remote drivers too */
1227  if (!strcmp(roottag, "enableBLOB"))
1228  crackBLOBHandling(dev, name, pcdataXMLEle(root), cp);
1229 
1230  /* build a new message -- set content iff anyone cares */
1231  mp = newMsg();
1232 
1233  /* send message to driver(s) responsible for dev */
1234  q2RDrivers(dev, mp, root);
1235 
1236  /* JM 2016-05-18: Upstream client can be a chained INDI server. If any driver locally is snooping
1237  * on any remote drivers, we should catch it and forward it to the responsible snooping driver. */
1238  /* send to snooping drivers. */
1239  // JM 2016-05-26: Only forward setXXX messages
1240  if (!strncmp(roottag, "set", 3))
1241  q2SDrivers(NULL, isblob, dev, name, mp, root);
1242 
1243  /* echo new* commands back to other clients */
1244  if (!strncmp(roottag, "new", 3))
1245  {
1246  if (q2Clients(cp, isblob, dev, name, mp, root) < 0)
1247  shutany++;
1248  }
1249 
1250  /* set message content if anyone cares else forget it */
1251  if (mp->count > 0)
1252  setMsgXMLEle(mp, root);
1253  else
1254  freeMsg(mp);
1255  delXMLEle(root);
1256  }
1257  else if (err[0])
1258  {
1259  char *ts = indi_tstamp(NULL);
1260  fprintf(stderr, "%s: Client %d: XML error: %s\n", ts, cp->s, err);
1261  fprintf(stderr, "%s: Client %d: XML read: %.*s\n", ts, cp->s, (int)nr, buf);
1262  shutdownClient(cp);
1263  return (-1);
1264  }
1265  }
1266 
1267  return (shutany ? -1 : 0);
1268 }
1269 
1270 /* read more from the given driver, send to each interested client when see
1271  * xml closure. if driver dies, try restarting.
1272  * return 0 if ok else -1 if had to shut down anything.
1273  */
1274 static int readFromDriver(DvrInfo *dp)
1275 {
1276  char buf[MAXRBUF];
1277  int shutany = 0;
1278  ssize_t nr;
1279  char err[1024];
1280  XMLEle **nodes;
1281  XMLEle *root;
1282  int inode = 0;
1283 
1284  /* read driver */
1285  nr = read(dp->rfd, buf, sizeof(buf));
1286  if (nr <= 0)
1287  {
1288  if (nr < 0)
1289  fprintf(stderr, "%s: Driver %s: stdin %s\n", indi_tstamp(NULL), dp->name, strerror(errno));
1290  else
1291  fprintf(stderr, "%s: Driver %s: stdin EOF\n", indi_tstamp(NULL), dp->name);
1292 
1293  shutdownDvr(dp, 1);
1294  return (-1);
1295  }
1296 
1297  /* process XML chunk */
1298  nodes = parseXMLChunk(dp->lp, buf, nr, err);
1299 
1300  if (!nodes)
1301  {
1302  if (err[0])
1303  {
1304  char *ts = indi_tstamp(NULL);
1305  fprintf(stderr, "%s: Driver %s: XML error: %s\n", ts, dp->name, err);
1306  fprintf(stderr, "%s: Driver %s: XML read: %.*s\n", ts, dp->name, (int)nr, buf);
1307  shutdownDvr(dp, 1);
1308  return (-1);
1309  }
1310  return -1;
1311  }
1312 
1313  root = nodes[inode];
1314  while (root)
1315  {
1316  char *roottag = tagXMLEle(root);
1317  const char *dev = findXMLAttValu(root, "device");
1318  const char *name = findXMLAttValu(root, "name");
1319  int isblob = !strcmp(tagXMLEle(root), "setBLOBVector");
1320  Msg *mp;
1321 
1322  if (verbose > 2)
1323  {
1324  fprintf(stderr, "%s: Driver %s: read ", indi_tstamp(0), dp->name);
1325  traceMsg(root);
1326  }
1327  else if (verbose > 1)
1328  {
1329  fprintf(stderr, "%s: Driver %s: read <%s device='%s' name='%s'>\n", indi_tstamp(NULL), dp->name,
1330  tagXMLEle(root), findXMLAttValu(root, "device"), findXMLAttValu(root, "name"));
1331  }
1332 
1333  /* that's all if driver is just registering a snoop */
1334  /* JM 2016-05-18: Send getProperties to upstream chained servers as well.*/
1335  if (!strcmp(roottag, "getProperties"))
1336  {
1337  addSDevice(dp, dev, name);
1338  mp = newMsg();
1339  /* send to interested chained servers upstream */
1340  if (q2Servers(dp, mp, root) < 0)
1341  shutany++;
1342  /* Send to snooped drivers if they exist so that they can echo back the snooped propertly immediately */
1343  q2RDrivers(dev, mp, root);
1344 
1345  if (mp->count > 0)
1346  setMsgXMLEle(mp, root);
1347  else
1348  freeMsg(mp);
1349  delXMLEle(root);
1350  inode++;
1351  root = nodes[inode];
1352  continue;
1353  }
1354 
1355  /* that's all if driver desires to snoop BLOBs from other drivers */
1356  if (!strcmp(roottag, "enableBLOB"))
1357  {
1358  Property *sp = findSDevice(dp, dev, name);
1359  if (sp)
1360  crackBLOB(pcdataXMLEle(root), &sp->blob);
1361  delXMLEle(root);
1362  inode++;
1363  root = nodes[inode];
1364  continue;
1365  }
1366 
1367  /* Found a new device? Let's add it to driver info */
1368  if (dev[0] && isDeviceInDriver(dev, dp) == 0)
1369  {
1370  dp->dev = (char **)realloc(dp->dev, (dp->ndev + 1) * sizeof(char *));
1371  dp->dev[dp->ndev] = (char *)malloc(MAXINDIDEVICE * sizeof(char));
1372 
1373  strncpy(dp->dev[dp->ndev], dev, MAXINDIDEVICE - 1);
1374  dp->dev[dp->ndev][MAXINDIDEVICE - 1] = '\0';
1375 
1376 #ifdef OSX_EMBEDED_MODE
1377  if (!dp->ndev)
1378  fprintf(stderr, "STARTED \"%s\"\n", dp->name);
1379  fflush(stderr);
1380 #endif
1381 
1382  dp->ndev++;
1383  }
1384 
1385  /* log messages if any and wanted */
1386  if (ldir)
1387  logDMsg(root, dev);
1388 
1389  /* build a new message -- set content iff anyone cares */
1390  mp = newMsg();
1391 
1392  /* send to interested clients */
1393  if (q2Clients(NULL, isblob, dev, name, mp, root) < 0)
1394  shutany++;
1395 
1396  /* send to snooping drivers */
1397  q2SDrivers(dp, isblob, dev, name, mp, root);
1398 
1399  /* set message content if anyone cares else forget it */
1400  if (mp->count > 0)
1401  setMsgXMLEle(mp, root);
1402  else
1403  freeMsg(mp);
1404  delXMLEle(root);
1405  inode++;
1406  root = nodes[inode];
1407  }
1408 
1409  free(nodes);
1410 
1411  return (shutany ? -1 : 0);
1412 }
1413 
1414 /* read more from the given driver stderr, add prefix and send to our stderr.
1415  * return 0 if ok else -1 if had to restart.
1416  */
1417 static int stderrFromDriver(DvrInfo *dp)
1418 {
1419  static char exbuf[MAXRBUF];
1420  static int nexbuf;
1421  ssize_t i, nr;
1422 
1423  /* read more */
1424  nr = read(dp->efd, exbuf + nexbuf, sizeof(exbuf) - nexbuf);
1425  if (nr <= 0)
1426  {
1427  if (nr < 0)
1428  fprintf(stderr, "%s: Driver %s: stderr %s\n", indi_tstamp(NULL), dp->name, strerror(errno));
1429  else
1430  fprintf(stderr, "%s: Driver %s: stderr EOF\n", indi_tstamp(NULL), dp->name);
1431  shutdownDvr(dp, 1);
1432  return (-1);
1433  }
1434  nexbuf += nr;
1435 
1436  /* prefix each whole line to our stderr, save extra for next time */
1437  for (i = 0; i < nexbuf; i++)
1438  {
1439  if (exbuf[i] == '\n')
1440  {
1441  fprintf(stderr, "%s: Driver %s: %.*s\n", indi_tstamp(NULL), dp->name, (int)i, exbuf);
1442  i++; /* count including nl */
1443  nexbuf -= i; /* remove from nexbuf */
1444  memmove(exbuf, exbuf + i, nexbuf); /* slide remaining to front */
1445  i = -1; /* restart for loop scan */
1446  }
1447  }
1448 
1449  return (0);
1450 }
1451 
1452 /* close down the given client */
1453 static void shutdownClient(ClInfo *cp)
1454 {
1455  Msg *mp;
1456 
1457  /* close connection */
1458  shutdown(cp->s, SHUT_RDWR);
1459  close(cp->s);
1460 
1461  /* free memory */
1462  delLilXML(cp->lp);
1463  free(cp->props);
1464 
1465  /* decrement and possibly free any unsent messages for this client */
1466  while ((mp = (Msg *)popFQ(cp->msgq)) != NULL)
1467  if (--mp->count == 0)
1468  freeMsg(mp);
1469  delFQ(cp->msgq);
1470 
1471  /* ok now to recycle */
1472  cp->active = 0;
1473 
1474  if (verbose > 0)
1475  fprintf(stderr, "%s: Client %d: shut down complete - bye!\n", indi_tstamp(NULL), cp->s);
1476 #ifdef OSX_EMBEDED_MODE
1477  int active = 0;
1478  for (int i = 0; i < nclinfo; i++)
1479  if (clinfo[i].active)
1480  active++;
1481  fprintf(stderr, "CLIENTS %d\n", active);
1482  fflush(stderr);
1483 #endif
1484 }
1485 
1486 /* close down the given driver and restart */
1487 static void shutdownDvr(DvrInfo *dp, int restart)
1488 {
1489  Msg *mp;
1490  int i = 0;
1491 
1492  // Tell client driver is dead.
1493  for (i = 0; i < dp->ndev; i++)
1494  {
1495  /* Inform clients that this driver is dead */
1496  XMLEle *root = addXMLEle(NULL, "delProperty");
1497  addXMLAtt(root, "device", dp->dev[i]);
1498 
1499  prXMLEle(stderr, root, 0);
1500  Msg *mp = newMsg();
1501 
1502  q2Clients(NULL, 0, dp->dev[i], NULL, mp, root);
1503  if (mp->count > 0)
1504  setMsgXMLEle(mp, root);
1505  else
1506  freeMsg(mp);
1507  delXMLEle(root);
1508  }
1509 
1510  /* make sure it's dead, reclaim resources */
1511  if (dp->pid == REMOTEDVR)
1512  {
1513  /* socket connection */
1514  shutdown(dp->wfd, SHUT_RDWR);
1515  close(dp->wfd); /* same as rfd */
1516  }
1517  else
1518  {
1519  /* local pipe connection */
1520  kill(dp->pid, SIGKILL); /* we've insured there are no zombies */
1521  close(dp->wfd);
1522  close(dp->rfd);
1523  close(dp->efd);
1524  }
1525 
1526 #ifdef OSX_EMBEDED_MODE
1527  fprintf(stderr, "STOPPED \"%s\"\n", dp->name);
1528  fflush(stderr);
1529 #endif
1530 
1531  /* free memory */
1532  free(dp->sprops);
1533  free(dp->dev);
1534  delLilXML(dp->lp);
1535 
1536  /* ok now to recycle */
1537  dp->active = 0;
1538  dp->ndev = 0;
1539 
1540  /* decrement and possibly free any unsent messages for this client */
1541  while ((mp = (Msg *)popFQ(dp->msgq)) != NULL)
1542  if (--mp->count == 0)
1543  freeMsg(mp);
1544  delFQ(dp->msgq);
1545 
1546  if (restart)
1547  {
1548  if (dp->restarts >= maxrestarts)
1549  {
1550  fprintf(stderr, "%s: Driver %s: Terminated after #%d restarts.\n", indi_tstamp(NULL), dp->name,
1551  dp->restarts);
1552  // If we're not in FIFO mode and we do not have any more drivers, shutdown the server
1553  terminateddrv++;
1554  if ((ndvrinfo - terminateddrv) <= 0 && !fifo.name)
1555  Bye();
1556  }
1557  else
1558  {
1559  fprintf(stderr, "%s: Driver %s: restart #%d\n", indi_tstamp(NULL), dp->name, ++dp->restarts);
1560  startDvr(dp);
1561  }
1562  }
1563 }
1564 
1565 /* put Msg mp on queue of each driver responsible for dev, or all drivers
1566  * if dev not specified.
1567  */
1568 static void q2RDrivers(const char *dev, Msg *mp, XMLEle *root)
1569 {
1570  DvrInfo *dp;
1571  char *roottag = tagXMLEle(root);
1572 
1573  char lastRemoteHost[MAXSBUF];
1574  int lastRemotePort = -1;
1575 
1576  /* queue message to each interested driver.
1577  * N.B. don't send generic getProps to more than one remote driver,
1578  * otherwise they all fan out and we get multiple responses back.
1579  */
1580  for (dp = dvrinfo; dp < &dvrinfo[ndvrinfo]; dp++)
1581  {
1582  int isRemote = (dp->pid == REMOTEDVR);
1583 
1584  if (dp->active == 0)
1585  continue;
1586 
1587  /* driver known to not support this dev */
1588  if (dev[0] && dev[0] != '*' && isDeviceInDriver(dev, dp) == 0)
1589  continue;
1590 
1591  /* Only send message to each *unique* remote driver at a particular host:port
1592  * Since it will be propogated to all other devices there */
1593  if (!dev[0] && isRemote && !strcmp(lastRemoteHost, dp->host) && lastRemotePort == dp->port)
1594  continue;
1595 
1596  /* JM 2016-10-30: Only send enableBLOB to remote drivers */
1597  if (isRemote == 0 && !strcmp(roottag, "enableBLOB"))
1598  continue;
1599 
1600  /* Retain last remote driver data so that we do not send the same info again to a driver
1601  * residing on the same host:port */
1602  if (isRemote)
1603  {
1604  strncpy(lastRemoteHost, dp->host, MAXSBUF);
1605  lastRemotePort = dp->port;
1606  }
1607 
1608  /* ok: queue message to this driver */
1609  mp->count++;
1610  pushFQ(dp->msgq, mp);
1611  if (verbose > 1)
1612  {
1613  fprintf(stderr, "%s: Driver %s: queuing responsible for <%s device='%s' name='%s'>\n", indi_tstamp(NULL),
1614  dp->name, tagXMLEle(root), findXMLAttValu(root, "device"), findXMLAttValu(root, "name"));
1615  }
1616  }
1617 }
1618 
1619 /* put Msg mp on queue of each driver snooping dev/name.
1620  * if BLOB always honor current mode.
1621  */
1622 static void q2SDrivers(DvrInfo *me, int isblob, const char *dev, const char *name, Msg *mp, XMLEle *root)
1623 {
1624  DvrInfo *dp = NULL;
1625 
1626  for (dp = dvrinfo; dp < &dvrinfo[ndvrinfo]; dp++)
1627  {
1628  if (dp->active == 0)
1629  continue;
1630 
1631  Property *sp = findSDevice(dp, dev, name);
1632 
1633  /* nothing for dp if not snooping for dev/name or wrong BLOB mode */
1634  if (!sp)
1635  continue;
1636  if ((isblob && sp->blob == B_NEVER) || (!isblob && sp->blob == B_ONLY))
1637  continue;
1638  if (me && me->pid == REMOTEDVR && dp->pid == REMOTEDVR)
1639  {
1640  // Do not send snoop data to remote drivers at the same host
1641  // since they will manage their own snoops remotely
1642  if (!strcmp(me->host, dp->host) && me->port == dp->port)
1643  continue;
1644  }
1645 
1646  /* ok: queue message to this device */
1647  mp->count++;
1648  pushFQ(dp->msgq, mp);
1649  if (verbose > 1)
1650  {
1651  fprintf(stderr, "%s: Driver %s: queuing snooped <%s device='%s' name='%s'>\n", indi_tstamp(NULL), dp->name,
1652  tagXMLEle(root), findXMLAttValu(root, "device"), findXMLAttValu(root, "name"));
1653  }
1654  }
1655 }
1656 
1657 /* add dev/name to dp's snooping list.
1658  * init with blob mode set to B_NEVER.
1659  */
1660 static void addSDevice(DvrInfo *dp, const char *dev, const char *name)
1661 {
1662  Property *sp;
1663  char *ip;
1664 
1665  /* no dups */
1666  sp = findSDevice(dp, dev, name);
1667  if (sp)
1668  return;
1669 
1670  /* add dev to sdevs list */
1671  dp->sprops = (Property *)realloc(dp->sprops, (dp->nsprops + 1) * sizeof(Property));
1672  sp = &dp->sprops[dp->nsprops++];
1673 
1674  ip = sp->dev;
1675  strncpy(ip, dev, MAXINDIDEVICE - 1);
1676  ip[MAXINDIDEVICE - 1] = '\0';
1677 
1678  ip = sp->name;
1679  strncpy(ip, name, MAXINDINAME - 1);
1680  ip[MAXINDINAME - 1] = '\0';
1681 
1682  sp->blob = B_NEVER;
1683 
1684  if (verbose)
1685  fprintf(stderr, "%s: Driver %s: snooping on %s.%s\n", indi_tstamp(NULL), dp->name, dev, name);
1686 }
1687 
1688 /* return Property if dp is snooping dev/name, else NULL.
1689  */
1690 static Property *findSDevice(DvrInfo *dp, const char *dev, const char *name)
1691 {
1692  int i;
1693 
1694  for (i = 0; i < dp->nsprops; i++)
1695  {
1696  Property *sp = &dp->sprops[i];
1697  if (!strcmp(sp->dev, dev) && (!sp->name[0] || !strcmp(sp->name, name)))
1698  return (sp);
1699  }
1700 
1701  return (NULL);
1702 }
1703 
1704 /* put Msg mp on queue of each client interested in dev/name, except notme.
1705  * if BLOB always honor current mode.
1706  * return -1 if had to shut down any clients, else 0.
1707  */
1708 static int q2Clients(ClInfo *notme, int isblob, const char *dev, const char *name, Msg *mp, XMLEle *root)
1709 {
1710  int shutany = 0;
1711  ClInfo *cp;
1712  int ql, i = 0;
1713 
1714  /* queue message to each interested client */
1715  for (cp = clinfo; cp < &clinfo[nclinfo]; cp++)
1716  {
1717  /* cp in use? notme? want this dev/name? blob? */
1718  if (!cp->active || cp == notme)
1719  continue;
1720  if (findClDevice(cp, dev, name) < 0)
1721  continue;
1722 
1723  //if ((isblob && cp->blob==B_NEVER) || (!isblob && cp->blob==B_ONLY))
1724  if (!isblob && cp->blob == B_ONLY)
1725  continue;
1726 
1727  if (isblob)
1728  {
1729  if (cp->nprops > 0)
1730  {
1731  Property *pp = NULL;
1732  int blob_found = 0;
1733  for (i = 0; i < cp->nprops; i++)
1734  {
1735  pp = &cp->props[i];
1736  if (!strcmp(pp->dev, dev) && (!strcmp(pp->name, name)))
1737  {
1738  blob_found = 1;
1739  break;
1740  }
1741  }
1742 
1743  if ((blob_found && pp->blob == B_NEVER) || (blob_found == 0 && cp->blob == B_NEVER))
1744  continue;
1745  }
1746  else if (cp->blob == B_NEVER)
1747  continue;
1748  }
1749 
1750  /* shut down this client if its q is already too large */
1751  ql = msgQSize(cp->msgq);
1752  if (isblob && maxstreamsiz > 0 && ql > maxstreamsiz)
1753  {
1754  // Drop frames for streaming blobs
1755  /* pull out each name/BLOB pair, decode */
1756  XMLEle *ep = NULL;
1757  int streamFound = 0;
1758  for (ep = nextXMLEle(root, 1); ep; ep = nextXMLEle(root, 0))
1759  {
1760  if (strcmp(tagXMLEle(ep), "oneBLOB") == 0)
1761  {
1762  XMLAtt *fa = findXMLAtt(ep, "format");
1763 
1764  if (fa && strstr(valuXMLAtt(fa), "stream"))
1765  {
1766  streamFound = 1;
1767  break;
1768  }
1769  }
1770  }
1771  if (streamFound)
1772  {
1773  if (verbose > 1)
1774  fprintf(stderr, "%s: Client %d: %d bytes behind. Dropping stream BLOB...\n", indi_tstamp(NULL),
1775  cp->s, ql);
1776  continue;
1777  }
1778  }
1779  if (ql > maxqsiz)
1780  {
1781  if (verbose)
1782  fprintf(stderr, "%s: Client %d: %d bytes behind, shutting down\n", indi_tstamp(NULL), cp->s, ql);
1783  shutdownClient(cp);
1784  shutany++;
1785  continue;
1786  }
1787 
1788  /* ok: queue message to this client */
1789  mp->count++;
1790  pushFQ(cp->msgq, mp);
1791  if (verbose > 1)
1792  fprintf(stderr, "%s: Client %d: queuing <%s device='%s' name='%s'>\n", indi_tstamp(NULL), cp->s,
1793  tagXMLEle(root), findXMLAttValu(root, "device"), findXMLAttValu(root, "name"));
1794  }
1795 
1796  return (shutany ? -1 : 0);
1797 }
1798 
1799 /* put Msg mp on queue of each chained server client, except notme.
1800  * return -1 if had to shut down any clients, else 0.
1801  */
1802 static int q2Servers(DvrInfo *me, Msg *mp, XMLEle *root)
1803 {
1804  int shutany = 0, i = 0, devFound = 0;
1805  ClInfo *cp;
1806  int ql = 0;
1807 
1808  /* queue message to each interested client */
1809  for (cp = clinfo; cp < &clinfo[nclinfo]; cp++)
1810  {
1811  /* cp in use? not chained server? */
1812  if (!cp->active)
1813  continue;
1814 
1815  // Only send the message to the upstream server that is connected specfically to the device in driver dp
1816  switch (cp->allprops)
1817  {
1818  // 0 --> not all props are requested. Check for specific combination
1819  case 0:
1820  for (i = 0; i < cp->nprops; i++)
1821  {
1822  Property *pp = &cp->props[i];
1823  int j = 0;
1824  for (j = 0; j < me->ndev; j++)
1825  {
1826  if (!strcmp(pp->dev, me->dev[j]))
1827  break;
1828  }
1829 
1830  if (j != me->ndev)
1831  {
1832  devFound = 1;
1833  break;
1834  }
1835  }
1836  break;
1837 
1838  // All props are requested. This is client-only mode (not upstream server)
1839  case 1:
1840  break;
1841  // Upstream server mode
1842  case 2:
1843  devFound = 1;
1844  break;
1845  }
1846 
1847  // If no matching device found, continue
1848  if (devFound == 0)
1849  continue;
1850 
1851  /* shut down this client if its q is already too large */
1852  ql = msgQSize(cp->msgq);
1853  if (ql > maxqsiz)
1854  {
1855  if (verbose)
1856  fprintf(stderr, "%s: Client %d: %d bytes behind, shutting down\n", indi_tstamp(NULL), cp->s, ql);
1857  shutdownClient(cp);
1858  shutany++;
1859  continue;
1860  }
1861 
1862  /* ok: queue message to this client */
1863  mp->count++;
1864  pushFQ(cp->msgq, mp);
1865  if (verbose > 1)
1866  fprintf(stderr, "%s: Client %d: queuing <%s device='%s' name='%s'>\n", indi_tstamp(NULL), cp->s,
1867  tagXMLEle(root), findXMLAttValu(root, "device"), findXMLAttValu(root, "name"));
1868  }
1869 
1870  return (shutany ? -1 : 0);
1871 }
1872 
1873 /* return size of all Msqs on the given q */
1874 static int msgQSize(FQ *q)
1875 {
1876  int i, l = 0;
1877 
1878  for (i = 0; i < nFQ(q); i++)
1879  {
1880  Msg *mp = (Msg *)peekiFQ(q, i);
1881  l += sizeof(Msg);
1882  if (mp->cp != mp->buf)
1883  l += mp->cl;
1884  }
1885 
1886  return (l);
1887 }
1888 
1889 /* print root as content in Msg mp.
1890  */
1891 static void setMsgXMLEle(Msg *mp, XMLEle *root)
1892 {
1893  /* want cl to only count content, but need room for final \0 */
1894  mp->cl = sprlXMLEle(root, 0);
1895  if (mp->cl < sizeof(mp->buf))
1896  mp->cp = mp->buf;
1897  else
1898  mp->cp = malloc(mp->cl + 1);
1899  sprXMLEle(mp->cp, root, 0);
1900 }
1901 
1902 /* save str as content in Msg mp.
1903  */
1904 static void setMsgStr(Msg *mp, char *str)
1905 {
1906  /* want cl to only count content, but need room for final \0 */
1907  mp->cl = strlen(str);
1908  if (mp->cl < sizeof(mp->buf))
1909  mp->cp = mp->buf;
1910  else
1911  mp->cp = malloc(mp->cl + 1);
1912  strcpy(mp->cp, str);
1913 }
1914 
1915 /* return pointer to one new nulled Msg
1916  */
1917 static Msg *newMsg(void)
1918 {
1919  return ((Msg *)calloc(1, sizeof(Msg)));
1920 }
1921 
1922 /* free Msg mp and everything it contains */
1923 static void freeMsg(Msg *mp)
1924 {
1925  if (mp->cp && mp->cp != mp->buf)
1926  free(mp->cp);
1927  free(mp);
1928 }
1929 
1930 /* write the next chunk of the current message in the queue to the given
1931  * client. pop message from queue when complete and free the message if we are
1932  * the last one to use it. shut down this client if trouble.
1933  * N.B. we assume we will never be called with cp->msgq empty.
1934  * return 0 if ok else -1 if had to shut down.
1935  */
1936 static int sendClientMsg(ClInfo *cp)
1937 {
1938  ssize_t nsend, nw;
1939  Msg *mp;
1940 
1941  /* get current message */
1942  mp = (Msg *)peekFQ(cp->msgq);
1943 
1944  /* send next chunk, never more than MAXWSIZ to reduce blocking */
1945  nsend = mp->cl - cp->nsent;
1946  if (nsend > MAXWSIZ)
1947  nsend = MAXWSIZ;
1948  nw = write(cp->s, &mp->cp[cp->nsent], nsend);
1949 
1950  /* shut down if trouble */
1951  if (nw <= 0)
1952  {
1953  if (nw == 0)
1954  fprintf(stderr, "%s: Client %d: write returned 0\n", indi_tstamp(NULL), cp->s);
1955  else
1956  fprintf(stderr, "%s: Client %d: write: %s\n", indi_tstamp(NULL), cp->s, strerror(errno));
1957  shutdownClient(cp);
1958  return (-1);
1959  }
1960 
1961  /* trace */
1962  if (verbose > 2)
1963  {
1964  fprintf(stderr, "%s: Client %d: sending msg copy %d nq %d:\n%.*s\n", indi_tstamp(NULL), cp->s, mp->count,
1965  nFQ(cp->msgq), (int)nw, &mp->cp[cp->nsent]);
1966  }
1967  else if (verbose > 1)
1968  {
1969  fprintf(stderr, "%s: Client %d: sending %.50s\n", indi_tstamp(NULL), cp->s, &mp->cp[cp->nsent]);
1970  }
1971 
1972  /* update amount sent. when complete: free message if we are the last
1973  * to use it and pop from our queue.
1974  */
1975  cp->nsent += nw;
1976  if (cp->nsent == mp->cl)
1977  {
1978  if (--mp->count == 0)
1979  freeMsg(mp);
1980  popFQ(cp->msgq);
1981  cp->nsent = 0;
1982  }
1983 
1984  return (0);
1985 }
1986 
1987 /* write the next chunk of the current message in the queue to the given
1988  * driver. pop message from queue when complete and free the message if we are
1989  * the last one to use it. restart this driver if touble.
1990  * N.B. we assume we will never be called with dp->msgq empty.
1991  * return 0 if ok else -1 if had to shut down.
1992  */
1993 static int sendDriverMsg(DvrInfo *dp)
1994 {
1995  ssize_t nsend, nw;
1996  Msg *mp;
1997 
1998  /* get current message */
1999  mp = (Msg *)peekFQ(dp->msgq);
2000 
2001  /* send next chunk, never more than MAXWSIZ to reduce blocking */
2002  nsend = mp->cl - dp->nsent;
2003  if (nsend > MAXWSIZ)
2004  nsend = MAXWSIZ;
2005  nw = write(dp->wfd, &mp->cp[dp->nsent], nsend);
2006 
2007  /* restart if trouble */
2008  if (nw <= 0)
2009  {
2010  if (nw == 0)
2011  fprintf(stderr, "%s: Driver %s: write returned 0\n", indi_tstamp(NULL), dp->name);
2012  else
2013  fprintf(stderr, "%s: Driver %s: write: %s\n", indi_tstamp(NULL), dp->name, strerror(errno));
2014  shutdownDvr(dp, 1);
2015  return (-1);
2016  }
2017 
2018  /* trace */
2019  if (verbose > 2)
2020  {
2021  fprintf(stderr, "%s: Driver %s: sending msg copy %d nq %d:\n%.*s\n", indi_tstamp(NULL), dp->name, mp->count,
2022  nFQ(dp->msgq), (int)nw, &mp->cp[dp->nsent]);
2023  }
2024  else if (verbose > 1)
2025  {
2026  fprintf(stderr, "%s: Driver %s: sending %.50s\n", indi_tstamp(NULL), dp->name, &mp->cp[dp->nsent]);
2027  }
2028 
2029  /* update amount sent. when complete: free message if we are the last
2030  * to use it and pop from our queue.
2031  */
2032  dp->nsent += nw;
2033  if (dp->nsent == mp->cl)
2034  {
2035  if (--mp->count == 0)
2036  freeMsg(mp);
2037  popFQ(dp->msgq);
2038  dp->nsent = 0;
2039  }
2040 
2041  return (0);
2042 }
2043 
2044 /* return 0 if cp may be interested in dev/name else -1
2045  */
2046 static int findClDevice(ClInfo *cp, const char *dev, const char *name)
2047 {
2048  int i;
2049 
2050  if (cp->allprops >= 1 || !dev[0])
2051  return (0);
2052  for (i = 0; i < cp->nprops; i++)
2053  {
2054  Property *pp = &cp->props[i];
2055  if (!strcmp(pp->dev, dev) && (!pp->name[0] || !strcmp(pp->name, name)))
2056  return (0);
2057  }
2058  return (-1);
2059 }
2060 
2061 /* add the given device and property to the devs[] list of client if new.
2062  */
2063 static void addClDevice(ClInfo *cp, const char *dev, const char *name, int isblob)
2064 {
2065  if (isblob)
2066  {
2067  int i = 0;
2068  for (i = 0; i < cp->nprops; i++)
2069  {
2070  Property *pp = &cp->props[i];
2071  if (!strcmp(pp->dev, dev) && (name == NULL || !strcmp(pp->name, name)))
2072  return;
2073  }
2074  }
2075  /* no dups */
2076  else if (!findClDevice(cp, dev, name))
2077  return;
2078 
2079  /* add */
2080  cp->props = (Property *)realloc(cp->props, (cp->nprops + 1) * sizeof(Property));
2081  Property *pp = &cp->props[cp->nprops++];
2082 
2083  /*ip = pp->dev;
2084  strncpy (ip, dev, MAXINDIDEVICE-1);
2085  ip[MAXINDIDEVICE-1] = '\0';
2086 
2087  ip = pp->name;
2088  strncpy (ip, name, MAXINDINAME-1);
2089  ip[MAXINDINAME-1] = '\0';*/
2090 
2091  strncpy(pp->dev, dev, MAXINDIDEVICE);
2092  strncpy(pp->name, name, MAXINDINAME);
2093  pp->blob = B_NEVER;
2094 }
2095 
2096 /* block to accept a new client arriving on lsocket.
2097  * return private nonblocking socket or exit.
2098  */
2099 static int newClSocket()
2100 {
2101  struct sockaddr_in cli_socket;
2102  socklen_t cli_len;
2103  int cli_fd;
2104 
2105  /* get a private connection to new client */
2106  cli_len = sizeof(cli_socket);
2107  cli_fd = accept(lsocket, (struct sockaddr *)&cli_socket, &cli_len);
2108  if (cli_fd < 0)
2109  {
2110  fprintf(stderr, "accept: %s\n", strerror(errno));
2111  Bye();
2112  }
2113 
2114  /* ok */
2115  return (cli_fd);
2116 }
2117 
2118 /* convert the string value of enableBLOB to our B_ state value.
2119  * no change if unrecognized
2120  */
2121 static void crackBLOB(const char *enableBLOB, BLOBHandling *bp)
2122 {
2123  if (!strcmp(enableBLOB, "Also"))
2124  *bp = B_ALSO;
2125  else if (!strcmp(enableBLOB, "Only"))
2126  *bp = B_ONLY;
2127  else if (!strcmp(enableBLOB, "Never"))
2128  *bp = B_NEVER;
2129 }
2130 
2131 /* Update the client property BLOB handling policy */
2132 static void crackBLOBHandling(const char *dev, const char *name, const char *enableBLOB, ClInfo *cp)
2133 {
2134  int i = 0;
2135 
2136  /* If we have EnableBLOB with property name, we add it to Client device list */
2137  if (name[0])
2138  addClDevice(cp, dev, name, 1);
2139  else
2140  /* Otherwise, we set the whole client blob handling to what's passed (enableBLOB) */
2141  crackBLOB(enableBLOB, &cp->blob);
2142 
2143  /* If whole client blob handling policy was updated, we need to pass that also to all children
2144  and if the request was for a specific property, then we apply the policy to it */
2145  for (i = 0; i < cp->nprops; i++)
2146  {
2147  Property *pp = &cp->props[i];
2148  if (!name[0])
2149  crackBLOB(enableBLOB, &pp->blob);
2150  else if (!strcmp(pp->dev, dev) && (!strcmp(pp->name, name)))
2151  {
2152  crackBLOB(enableBLOB, &pp->blob);
2153  return;
2154  }
2155  }
2156 }
2157 
2158 /* print key attributes and values of the given xml to stderr.
2159  */
2160 static void traceMsg(XMLEle *root)
2161 {
2162  static const char *prtags[] =
2163  {
2164  "defNumber", "oneNumber", "defText", "oneText", "defSwitch", "oneSwitch", "defLight", "oneLight",
2165  };
2166  XMLEle *e;
2167  const char *msg, *perm, *pcd;
2168  unsigned int i;
2169 
2170  /* print tag header */
2171  fprintf(stderr, "%s %s %s %s", tagXMLEle(root), findXMLAttValu(root, "device"), findXMLAttValu(root, "name"),
2172  findXMLAttValu(root, "state"));
2173  pcd = pcdataXMLEle(root);
2174  if (pcd[0])
2175  fprintf(stderr, " %s", pcd);
2176  perm = findXMLAttValu(root, "perm");
2177  if (perm[0])
2178  fprintf(stderr, " %s", perm);
2179  msg = findXMLAttValu(root, "message");
2180  if (msg[0])
2181  fprintf(stderr, " '%s'", msg);
2182 
2183  /* print each array value */
2184  for (e = nextXMLEle(root, 1); e; e = nextXMLEle(root, 0))
2185  for (i = 0; i < sizeof(prtags) / sizeof(prtags[0]); i++)
2186  if (strcmp(prtags[i], tagXMLEle(e)) == 0)
2187  fprintf(stderr, "\n %10s='%s'", findXMLAttValu(e, "name"), pcdataXMLEle(e));
2188 
2189  fprintf(stderr, "\n");
2190 }
2191 
2192 /* fill s with current UT string.
2193  * if no s, use a static buffer
2194  * return s or buffer.
2195  * N.B. if use our buffer, be sure to use before calling again
2196  */
2197 static char *indi_tstamp(char *s)
2198 {
2199  static char sbuf[64];
2200  struct tm *tp;
2201  time_t t;
2202 
2203  time(&t);
2204  tp = gmtime(&t);
2205  if (!s)
2206  s = sbuf;
2207  strftime(s, sizeof(sbuf), "%Y-%m-%dT%H:%M:%S", tp);
2208  return (s);
2209 }
2210 
2211 /* log message in root known to be from device dev to ldir, if any.
2212  */
2213 static void logDMsg(XMLEle *root, const char *dev)
2214 {
2215  char stamp[64];
2216  char logfn[1024];
2217  const char *ts, *ms;
2218  FILE *fp;
2219 
2220  /* get message, if any */
2221  ms = findXMLAttValu(root, "message");
2222  if (!ms[0])
2223  return;
2224 
2225  /* get timestamp now if not provided */
2226  ts = findXMLAttValu(root, "timestamp");
2227  if (!ts[0])
2228  {
2229  indi_tstamp(stamp);
2230  ts = stamp;
2231  }
2232 
2233  /* append to log file, name is date portion of time stamp */
2234  sprintf(logfn, "%s/%.10s.islog", ldir, ts);
2235  fp = fopen(logfn, "a");
2236  if (!fp)
2237  return; /* oh well */
2238  fprintf(fp, "%s: %s: %s\n", ts, dev, ms);
2239  fclose(fp);
2240 }
2241 
2242 /* log when then exit */
2243 static void Bye()
2244 {
2245  fprintf(stderr, "%s: good bye\n", indi_tstamp(NULL));
2246  exit(1);
2247 }
MAXINDIDEVICE
#define MAXINDIDEVICE
Definition: indiapi.h:192
peekFQ
void * peekFQ(FQ *q)
Definition: fq.c:102
INDIPORT
#define INDIPORT
Definition: indiserver.c:75
MAXWSIZ
#define MAXWSIZ
Definition: indiserver.c:79
DvrInfo::msgq
FQ * msgq
Definition: indiserver.c:159
Msg::cp
char * cp
Definition: indiserver.c:95
ClInfo
Definition: indiserver.c:122
Msg
Definition: indiserver.c:91
newLilXML
LilXML * newLilXML()
Create a new lilxml parser.
Definition: lilxml.c:148
peekiFQ
void * peekiFQ(FQ *q, int i)
Definition: fq.c:112
cmd
__u8 cmd[4]
Definition: pwc-ioctl.h:4
DvrInfo::envConfig
char envConfig[MAXSBUF]
Definition: indiserver.c:142
DvrInfo::efd
int efd
Definition: indiserver.c:156
DEFMAXQSIZ
#define DEFMAXQSIZ
Definition: indiserver.c:81
sprlXMLEle
int sprlXMLEle(XMLEle *ep, int level)
return number of bytes in a string guaranteed able to hold result of sprXLMEle(ep) (sans trailing \0@...
Definition: lilxml.c:776
pcdataXMLEle
char * pcdataXMLEle(XMLEle *ep)
Return the pcdata of an XML element.
Definition: lilxml.c:575
indiapi.h
Constants and Data structure definitions for the interface to the reference INDI C API implementation...
nextXMLEle
XMLEle * nextXMLEle(XMLEle *ep, int init)
Iterate an XML element for a list of nesetd XML elements.
Definition: lilxml.c:524
ClInfo::nprops
int nprops
Definition: indiserver.c:126
MAXINDINAME
#define MAXINDINAME
Definition: indiapi.h:190
delFQ
void delFQ(FQ *q)
Definition: fq.c:81
DvrInfo::rfd
int rfd
Definition: indiserver.c:154
B_ONLY
@ B_ONLY
Definition: indidevapi.h:272
ClInfo::allprops
int allprops
Definition: indiserver.c:127
Property::dev
char dev[MAXINDIDEVICE]
Definition: indiserver.c:102
Property::name
char name[MAXINDINAME]
Definition: indiserver.c:103
INDI_UNUSED
#define INDI_UNUSED(x)
Definition: indidevapi.h:799
MAXSBUF
#define MAXSBUF
Definition: indiserver.c:77
ClInfo::blob
BLOBHandling blob
Definition: indiserver.c:128
popFQ
void * popFQ(FQ *q)
Definition: fq.c:96
DvrInfo::envDev
char envDev[MAXSBUF]
Definition: indiserver.c:141
addXMLAtt
XMLAtt * addXMLAtt(XMLEle *ep, const char *name, const char *valu)
Add an XML attribute to an existing XML element.
Definition: lilxml.c:664
DvrInfo::envPrefix
char envPrefix[MAXSBUF]
Definition: indiserver.c:144
DvrInfo::wfd
int wfd
Definition: indiserver.c:155
SHORTMSGSIZ
#define SHORTMSGSIZ
Definition: indiserver.c:80
Property::blob
BLOBHandling blob
Definition: indiserver.c:104
fq.h
readXMLEle
XMLEle * readXMLEle(LilXML *lp, int newc, char ynot[])
Process an XML one char at a time.
Definition: lilxml.c:383
DvrInfo::nsent
unsigned int nsent
Definition: indiserver.c:160
DvrInfo::restarts
int restarts
Definition: indiserver.c:157
INDIV
#define INDIV
Definition: indiapi.h:133
delLilXML
void delLilXML(LilXML *lp)
Delete a lilxml parser.
Definition: lilxml.c:157
ClInfo::props
Property * props
Definition: indiserver.c:125
DvrInfo::envSkel
char envSkel[MAXSBUF]
Definition: indiserver.c:143
indidevapi.h
Interface to the reference INDI C API device implementation on the Device Driver side.
DvrInfo::sprops
Property * sprops
Definition: indiserver.c:151
ClInfo::msgq
FQ * msgq
Definition: indiserver.c:131
DvrInfo::pid
int pid
Definition: indiserver.c:153
DvrInfo::nsprops
int nsprops
Definition: indiserver.c:152
DvrInfo::lp
LilXML * lp
Definition: indiserver.c:158
B_ALSO
@ B_ALSO
Definition: indidevapi.h:271
Msg::cl
unsigned long cl
Definition: indiserver.c:94
xml_att_
Definition: lilxml.c:120
_FQ
Definition: fq.c:51
pushFQ
void pushFQ(FQ *q, void *e)
Definition: fq.c:88
Msg::count
int count
Definition: indiserver.c:93
Property
Definition: indiserver.c:100
B_NEVER
@ B_NEVER
Definition: indidevapi.h:270
tagXMLEle
char * tagXMLEle(XMLEle *ep)
Return the tag of an XML element.
Definition: lilxml.c:569
xml_ele_
Definition: lilxml.c:105
BLOBHandling
BLOBHandling
How drivers handle BLOBs incoming from snooping drivers.
Definition: indidevapi.h:268
newFQ
FQ * newFQ(int grow)
Definition: fq.c:71
lilxml.h
A little DOM-style library to handle parsing and processing an XML file.
fd
int fd
Definition: intelliscope.c:43
DvrInfo::host
char host[MAXSBUF]
Definition: indiserver.c:145
nFQ
int nFQ(FQ *q)
Definition: fq.c:118
REMOTEDVR
#define REMOTEDVR
Definition: indiserver.c:76
parseXMLChunk
XMLEle ** parseXMLChunk(LilXML *lp, char *buf, int size, char ynot[])
Process an XML chunk.
Definition: lilxml.c:213
valuXMLAtt
char * valuXMLAtt(XMLAtt *ap)
Return the value of an XML attribute.
Definition: lilxml.c:593
main
int main(int ac, char *av[])
Definition: indiserver.c:218
DvrInfo::name
char name[MAXINDINAME]
Definition: indiserver.c:140
prXMLEle
void prXMLEle(FILE *fp, XMLEle *ep, int level)
Print an XML element.
Definition: lilxml.c:699
ClInfo::nsent
unsigned int nsent
Definition: indiserver.c:132
ClInfo::lp
LilXML * lp
Definition: indiserver.c:130
ClInfo::active
int active
Definition: indiserver.c:124
verbose
int verbose
Definition: indidriver.c:49
findXMLAttValu
const char * findXMLAttValu(XMLEle *ep, const char *name)
Find an XML element's attribute value.
Definition: lilxml.c:613
DvrInfo::dev
char ** dev
Definition: indiserver.c:148
DEFMAXRESTART
#define DEFMAXRESTART
Definition: indiserver.c:83
LilXML_
Definition: lilxml.c:91
DvrInfo::active
int active
Definition: indiserver.c:150
sprXMLEle
int sprXMLEle(char *s, XMLEle *ep, int level)
sample print ep to string s. N.B. s must be at least as large as that reported by sprlXMLEle()+1....
Definition: lilxml.c:735
me
char * me
Definition: indidriver.c:50
DvrInfo::ndev
int ndev
Definition: indiserver.c:149
DvrInfo::port
int port
Definition: indiserver.c:146
Msg::buf
char buf[SHORTMSGSIZ]
Definition: indiserver.c:96
DvrInfo
Definition: indiserver.c:138
addXMLEle
XMLEle * addXMLEle(XMLEle *parent, const char *tag)
add an element with the given tag to the given element. parent can be NULL to make a new root.
Definition: lilxml.c:639
delXMLEle
void delXMLEle(XMLEle *ep)
delXMLEle Delete XML element.
Definition: lilxml.c:165
errno
int errno
MAXRBUF
#define MAXRBUF
Definition: indiserver.c:78
findXMLAtt
XMLAtt * findXMLAtt(XMLEle *ep, const char *name)
Find an XML attribute within an XML element.
Definition: lilxml.c:493
fifo
struct @157 fifo
DEFMAXSSIZ
#define DEFMAXSSIZ
Definition: indiserver.c:82
ClInfo::s
int s
Definition: indiserver.c:129