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