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