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