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