source: BAORadio/libindi/v1.0.1/indiserver.c@ 680

Last change on this file since 680 was 501, checked in by frichard, 15 years ago

-BAOControl : petite interface permettant de contrôler les antennes via le pilote indi_BAO
-Le pilote indi_BAO utilise désormais libindi v 0.7

File size: 48.6 KB
Line 
1/* INDI Server for protocol version 1.7.
2 * Copyright (C) 2007 Elwood C. Downey ecdowney@clearskyinstitute.com
3
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 * Implementation notes:
33 *
34 * We fork each driver and open a server socket listening for INDI clients.
35 * Then forever we listen for new clients and pass traffic between clients and
36 * drivers, subject to optimizations based on sniffing messages for matching
37 * Devices and Properties. Since one message might be destined to more than
38 * one client or device, they are queued and only removed after the last
39 * consumer is finished. XMLEle are converted to linear strings before being
40 * sent to optimize write system calls and avoid blocking to slow clients.
41 * Clients that get more than maxqsiz bytes behind are shut down.
42 */
43
44#include <stdio.h>
45#include <stdlib.h>
46#include <stdarg.h>
47#include <signal.h>
48#include <string.h>
49#include <errno.h>
50#include <unistd.h>
51#include <time.h>
52#include <fcntl.h>
53#include <sys/time.h>
54#include <sys/types.h>
55#include <sys/stat.h>
56#include <sys/socket.h>
57#include <netinet/in.h>
58#include <netdb.h>
59#include <arpa/inet.h>
60
61#include "lilxml.h"
62#include "indiapi.h"
63#include "fq.h"
64
65#define INDIPORT 7624 /* default TCP/IP port to listen */
66#define REMOTEDVR (-1234) /* invalid PID to flag remote drivers */
67#define MAXRBUF 4096 /* max read buffering here */
68#define MAXWSIZ 4096 /* max bytes/write */
69#define DEFMAXQSIZ 10 /* default max q behind, MB */
70
71/* associate a usage count with queuded client or device message */
72typedef struct {
73 int count; /* number of consumers left */
74 unsigned int cl; /* content length */
75 char *cp; /* content: buf or malloced */
76 char buf[MAXWSIZ]; /* local buf for most messages */
77} Msg;
78
79/* BLOB handling, NEVER is the default */
80typedef enum {B_NEVER=0, B_ALSO, B_ONLY} BLOBHandling;
81
82/* device + property name */
83typedef struct {
84 char dev[MAXINDIDEVICE];
85 char name[MAXINDINAME];
86} Property;
87
88/* record of each snooped property */
89typedef struct {
90 Property prop;
91 BLOBHandling blob; /* when to snoop BLOBs */
92} Snoopee;
93
94struct
95{
96 const char *name; /* Path to FIFO for dynamic startups & shutdowns of drivers */
97 int fd;
98 FILE *fs;
99} fifo;
100
101/* info for each connected client */
102typedef struct {
103 int active; /* 1 when this record is in use */
104 Property *props; /* malloced array of props we want */
105 int nprops; /* n entries in props[] */
106 int allprops; /* saw getProperties w/o device */
107 BLOBHandling blob; /* when to send setBLOBs */
108 int s; /* socket for this client */
109 LilXML *lp; /* XML parsing context */
110 FQ *msgq; /* Msg queue */
111 unsigned int nsent; /* bytes of current Msg sent so far */
112} ClInfo;
113static ClInfo *clinfo; /* malloced pool of clients */
114static int nclinfo; /* n total (not active) */
115
116/* info for each connected driver */
117typedef struct {
118 char name[MAXINDIDEVICE]; /* persistent name */
119 char dev[MAXINDIDEVICE]; /* device served by this driver */
120 int active; /* 1 when this record is in use */
121 Snoopee *sprops; /* malloced array of props we snoop */
122 int nsprops; /* n entries in sprops[] */
123 int pid; /* process id or REMOTEDVR if remote */
124 int rfd; /* read pipe fd */
125 int wfd; /* write pipe fd */
126 int efd; /* stderr from driver, if local */
127 int restarts; /* times process has been restarted */
128 LilXML *lp; /* XML parsing context */
129 FQ *msgq; /* Msg queue */
130 unsigned int nsent; /* bytes of current Msg sent so far */
131} DvrInfo;
132static DvrInfo *dvrinfo; /* malloced array of drivers */
133static int ndvrinfo; /* n total */
134
135static char *me; /* our name */
136static int port = INDIPORT; /* public INDI port */
137static int verbose; /* chattiness */
138static int lsocket; /* listen socket */
139static char *ldir; /* where to log driver messages */
140static int maxqsiz = (DEFMAXQSIZ*1024*1024); /* kill if these bytes behind */
141
142static void logStartup(int ac, char *av[]);
143static void usage (void);
144static void noZombies (void);
145static void noSIGPIPE (void);
146static void indiFIFO(void);
147static void indiRun (void);
148static void indiListen (void);
149static void newFIFO(void);
150static void newClient (void);
151static int newClSocket (void);
152static void shutdownClient (ClInfo *cp);
153static int readFromClient (ClInfo *cp);
154static void startDvr (DvrInfo *dp);
155static void startLocalDvr (DvrInfo *dp);
156static void startRemoteDvr (DvrInfo *dp);
157static int openINDIServer (char host[], int indi_port);
158static void shutdownDvr (DvrInfo *dp, int restart);
159static void q2RDrivers (const char *dev, Msg *mp, XMLEle *root);
160static void q2SDrivers (int isblob, const char *dev, const char *name, Msg *mp,
161 XMLEle *root);
162static int q2Clients (ClInfo *notme, int isblob, const char *dev, const char *name,
163 Msg *mp, XMLEle *root);
164static void addSDevice (DvrInfo *dp, const char *dev, const char *name);
165static Snoopee *findSDevice (DvrInfo *dp, const char *dev, const char *name);
166static void addClDevice (ClInfo *cp, const char *dev, const char *name);
167static int findClDevice (ClInfo *cp, const char *dev, const char *name);
168static int readFromDriver (DvrInfo *dp);
169static int stderrFromDriver (DvrInfo *dp);
170static int msgQSize (FQ *q);
171static void setMsgXMLEle (Msg *mp, XMLEle *root);
172static void setMsgStr (Msg *mp, char *str);
173static void freeMsg (Msg *mp);
174static Msg *newMsg (void);
175static int sendClientMsg (ClInfo *cp);
176static int sendDriverMsg (DvrInfo *cp);
177static void crackBLOB (char *enableBLOB, BLOBHandling *bp);
178static void traceMsg (XMLEle *root);
179static char *indi_tstamp (char *s);
180static void logDMsg (XMLEle *root, const char *dev);
181static void Bye(void);
182
183int
184main (int ac, char *av[])
185{
186 /* log startup */
187 logStartup(ac, av);
188
189 /* save our name */
190 me = av[0];
191
192 /* crack args */
193 while ((--ac > 0) && ((*++av)[0] == '-')) {
194 char *s;
195 for (s = av[0]+1; *s != '\0'; s++)
196 switch (*s) {
197 case 'l':
198 if (ac < 2) {
199 fprintf (stderr, "-l requires log directory\n");
200 usage();
201 }
202 ldir = *++av;
203 ac--;
204 break;
205 case 'm':
206 if (ac < 2) {
207 fprintf (stderr, "-m requires max MB behind\n");
208 usage();
209 }
210 maxqsiz = 1024*1024*atoi(*++av);
211 ac--;
212 break;
213 case 'p':
214 if (ac < 2) {
215 fprintf (stderr, "-p requires port value\n");
216 usage();
217 }
218 port = atoi(*++av);
219 ac--;
220 break;
221 case 'f':
222 if (ac < 2) {
223 fprintf (stderr, "-f requires fifo node\n");
224 usage();
225 }
226 fifo.name = *++av;
227 ac--;
228 break;
229 case 'v':
230 verbose++;
231 break;
232 default:
233 usage();
234 }
235 }
236
237 /* at this point there are ac args in av[] to name our drivers */
238 if (ac == 0 && !fifo.name)
239 usage();
240
241 /* take care of some unixisms */
242 noZombies();
243 noSIGPIPE();
244
245 /* realloc seed for client pool */
246 clinfo = (ClInfo *) malloc (1);
247 nclinfo = 0;
248
249 /* create driver info array all at once since size never changes */
250 ndvrinfo = ac;
251 dvrinfo = (DvrInfo *) calloc (ndvrinfo, sizeof(DvrInfo));
252
253 /* start each driver */
254 while (ac-- > 0) {
255 strcpy(dvrinfo[ac].name, *av++);
256 startDvr (&dvrinfo[ac]);
257 }
258
259 /* announce we are online */
260 indiListen();
261
262 /* Load up FIFO, if available */
263 indiFIFO();
264
265 /* handle new clients and all io */
266 while (1)
267 indiRun();
268
269 /* whoa! */
270 fprintf (stderr, "unexpected return from main\n");
271 return (1);
272}
273
274/* record we have started and our args */
275static void
276logStartup(int ac, char *av[])
277{
278 int i;
279
280 fprintf (stderr, "%s: startup: ", indi_tstamp(NULL));
281 for (i = 0; i < ac; i++)
282 fprintf (stderr, "%s ", av[i]);
283 fprintf (stderr, "\n");
284}
285
286/* print usage message and exit (2) */
287static void
288usage(void)
289{
290 fprintf (stderr, "Usage: %s [options] driver [driver ...]\n", me);
291 fprintf (stderr, "Purpose: server for local and remote INDI drivers\n");
292 fprintf (stderr, "Code %s. Protocol %g.\n", "$Revision: 726523 $", INDIV);
293 fprintf (stderr, "Options:\n");
294 fprintf (stderr, " -l d : log driver messages to <d>/YYYY-MM-DD.islog\n");
295 fprintf (stderr, " -m m : kill client if gets more than this many MB behind, default %d\n", DEFMAXQSIZ);
296 fprintf (stderr, " -p p : alternate IP port, default %d\n", INDIPORT);
297 fprintf (stderr, " -f path : Path to fifo for dynamic startup and shutdown of drivers.\n");
298 fprintf (stderr, " -v : show key events, no traffic\n");
299 fprintf (stderr, " -vv : -v + key message content\n");
300 fprintf (stderr, " -vvv : -vv + complete xml\n");
301 fprintf (stderr, "driver : executable or device@host[:port]\n");
302
303 exit (2);
304}
305
306/* arrange for no zombies if drivers die */
307static void
308noZombies()
309{
310 struct sigaction sa;
311 sa.sa_handler = SIG_IGN;
312 sigemptyset(&sa.sa_mask);
313#ifdef SA_NOCLDWAIT
314 sa.sa_flags = SA_NOCLDWAIT;
315#else
316 sa.sa_flags = 0;
317#endif
318 (void)sigaction(SIGCHLD, &sa, NULL);
319}
320
321/* turn off SIGPIPE on bad write so we can handle it inline */
322static void
323noSIGPIPE()
324{
325 struct sigaction sa;
326 sa.sa_handler = SIG_IGN;
327 sigemptyset(&sa.sa_mask);
328 (void)sigaction(SIGPIPE, &sa, NULL);
329}
330
331static DvrInfo * allocDvr ()
332{
333 DvrInfo *dp = NULL;
334 int dvi;
335
336/* try to reuse a drivber slot, else add one */
337for (dvi = 0; dvi < ndvrinfo; dvi++)
338 if (!(dp = &dvrinfo[dvi])->active)
339 break;
340if (dvi == ndvrinfo)
341{
342 /* grow dvrinfo */
343 dvrinfo = (DvrInfo *) realloc (dvrinfo, (ndvrinfo+1)*sizeof(DvrInfo));
344 if (!dvrinfo) {
345 fprintf (stderr, "no memory for new drivers\n");
346 Bye();
347 }
348 dp = &dvrinfo[ndvrinfo++];
349}
350
351/* rig up new dvrinfo entry */
352memset (dp, 0, sizeof(*dp));
353dp->active = 1;
354
355return dp;
356
357}
358
359/* start the given INDI driver process or connection.
360 * exit if trouble.
361 */
362static void
363startDvr (DvrInfo *dp)
364{
365 if (strchr (dp->name, '@'))
366 startRemoteDvr (dp);
367 else
368 startLocalDvr (dp);
369}
370
371/* start the given local INDI driver process.
372 * exit if trouble.
373 */
374static void
375startLocalDvr (DvrInfo *dp)
376{
377 Msg *mp;
378 char buf[1024];
379 int rp[2], wp[2], ep[2];
380 int pid;
381
382 /* build three pipes: r, w and error*/
383 if (pipe (rp) < 0) {
384 fprintf (stderr, "%s: read pipe: %s\n", indi_tstamp(NULL),
385 strerror(errno));
386 Bye();
387 }
388 if (pipe (wp) < 0) {
389 fprintf (stderr, "%s: write pipe: %s\n", indi_tstamp(NULL),
390 strerror(errno));
391 Bye();
392 }
393 if (pipe (ep) < 0) {
394 fprintf (stderr, "%s: stderr pipe: %s\n", indi_tstamp(NULL),
395 strerror(errno));
396 Bye();
397 }
398
399 /* fork&exec new process */
400 pid = fork();
401 if (pid < 0) {
402 fprintf (stderr, "%s: fork: %s\n", indi_tstamp(NULL), strerror(errno));
403 Bye();
404 }
405 if (pid == 0) {
406 /* child: exec name */
407 int fd;
408
409 /* rig up pipes */
410 dup2 (wp[0], 0); /* driver stdin reads from wp[0] */
411 dup2 (rp[1], 1); /* driver stdout writes to rp[1] */
412 dup2 (ep[1], 2); /* driver stderr writes to e[]1] */
413 for (fd = 3; fd < 100; fd++)
414 (void) close (fd);
415
416 /* go -- should never return */
417 execlp (dp->name, dp->name, NULL);
418 fprintf (stderr, "%s: Driver %s: execlp: %s\n", indi_tstamp(NULL),
419 dp->name, strerror(errno));
420 _exit (1); /* parent will notice EOF shortly */
421 }
422
423 /* don't need child's side of pipes */
424 close (wp[0]);
425 close (rp[1]);
426 close (ep[1]);
427
428 /* record pid, io channels, init lp and snoop list */
429 dp->pid = pid;
430 dp->rfd = rp[0];
431 dp->wfd = wp[1];
432 dp->efd = ep[0];
433 dp->lp = newLilXML();
434 dp->msgq = newFQ(1);
435 dp->sprops = (Snoopee*) malloc (1); /* seed for realloc */
436 dp->nsprops = 0;
437 dp->nsent = 0;
438 dp->active = 1;
439
440 /* first message primes driver to report its properties -- dev known
441 * if restarting
442 */
443 mp = newMsg();
444 pushFQ (dp->msgq, mp);
445 if (dp->dev[0])
446 sprintf (buf, "<getProperties device='%s' version='%g'/>\n",
447 dp->dev, INDIV);
448 else
449 sprintf (buf, "<getProperties version='%g'/>\n", INDIV);
450 setMsgStr (mp, buf);
451 mp->count++;
452
453 if (verbose > 0)
454 fprintf (stderr, "%s: Driver %s: pid=%d rfd=%d wfd=%d efd=%d\n",
455 indi_tstamp(NULL), dp->name, dp->pid, dp->rfd, dp->wfd, dp->efd);
456}
457
458/* start the given remote INDI driver connection.
459 * exit if trouble.
460 */
461static void
462startRemoteDvr (DvrInfo *dp)
463{
464 Msg *mp;
465 char dev[1024];
466 char host[1024];
467 char buf[1024];
468 int indi_port, sockfd;
469
470 /* extract host and port */
471 indi_port = INDIPORT;
472 if (sscanf (dp->name, "%[^@]@%[^:]:%d", dev, host, &indi_port) < 2) {
473 fprintf (stderr, "Bad remote device syntax: %s\n", dp->name);
474 Bye();
475 }
476
477 /* connect */
478 sockfd = openINDIServer (host, indi_port);
479
480 /* record flag pid, io channels, init lp and snoop list */
481 dp->pid = REMOTEDVR;
482 dp->rfd = sockfd;
483 dp->wfd = sockfd;
484 dp->lp = newLilXML();
485 dp->msgq = newFQ(1);
486 dp->sprops = (Snoopee*) malloc (1); /* seed for realloc */
487 dp->nsprops = 0;
488 dp->nsent = 0;
489 dp->active = 1;
490
491 /* N.B. storing name now is key to limiting outbound traffic to this
492 * dev.
493 */
494 strncpy (dp->dev, dev, MAXINDIDEVICE-1);
495 dp->dev[MAXINDIDEVICE-1] = '\0';
496
497 /* Sending getProperties with device lets remote server limit its
498 * outbound (and our inbound) traffic on this socket to this device.
499 */
500 mp = newMsg();
501 pushFQ (dp->msgq, mp);
502 sprintf (buf, "<getProperties device='%s' version='%g'/>\n",
503 dp->dev, INDIV);
504 setMsgStr (mp, buf);
505 mp->count++;
506
507 if (verbose > 0)
508 fprintf (stderr, "%s: Driver %s: socket=%d\n", indi_tstamp(NULL),
509 dp->name, sockfd);
510}
511
512/* open a connection to the given host and port or die.
513 * return socket fd.
514 */
515static int
516openINDIServer (char host[], int indi_port)
517{
518 struct sockaddr_in serv_addr;
519 struct hostent *hp;
520 int sockfd;
521
522 /* lookup host address */
523 hp = gethostbyname (host);
524 if (!hp) {
525 fprintf (stderr, "gethostbyname(%s): %s\n", host, strerror(errno));
526 Bye();
527 }
528
529 /* create a socket to the INDI server */
530 (void) memset ((char *)&serv_addr, 0, sizeof(serv_addr));
531 serv_addr.sin_family = AF_INET;
532 serv_addr.sin_addr.s_addr =
533 ((struct in_addr *)(hp->h_addr_list[0]))->s_addr;
534 serv_addr.sin_port = htons(indi_port);
535 if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
536 fprintf (stderr, "socket(%s,%d): %s\n", host, indi_port,strerror(errno));
537 Bye();
538 }
539
540 /* connect */
541 if (connect (sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr))<0){
542 fprintf (stderr, "connect(%s,%d): %s\n", host,indi_port,strerror(errno));
543 Bye();
544 }
545
546 /* ok */
547 return (sockfd);
548}
549
550/* create the public INDI Driver endpoint lsocket on port.
551 * return server socket else exit.
552 */
553static void
554indiListen ()
555{
556 struct sockaddr_in serv_socket;
557 int sfd;
558 int reuse = 1;
559
560 /* make socket endpoint */
561 if ((sfd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
562 fprintf (stderr, "%s: socket: %s\n", indi_tstamp(NULL), strerror(errno));
563 Bye();
564 }
565
566 /* bind to given port for any IP address */
567 memset (&serv_socket, 0, sizeof(serv_socket));
568 serv_socket.sin_family = AF_INET;
569 #ifdef SSH_TUNNEL
570 serv_socket.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
571 #else
572 serv_socket.sin_addr.s_addr = htonl (INADDR_ANY);
573 #endif
574 serv_socket.sin_port = htons ((unsigned short)port);
575 if (setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)) < 0){
576 fprintf (stderr, "%s: setsockopt: %s\n", indi_tstamp(NULL),
577 strerror(errno));
578 Bye();
579 }
580 if (bind(sfd,(struct sockaddr*)&serv_socket,sizeof(serv_socket)) < 0){
581 fprintf (stderr, "%s: bind: %s\n", indi_tstamp(NULL), strerror(errno));
582 Bye();
583 }
584
585 /* willing to accept connections with a backlog of 5 pending */
586 if (listen (sfd, 5) < 0) {
587 fprintf (stderr, "%s: listen: %s\n", indi_tstamp(NULL), strerror(errno));
588 Bye();
589 }
590
591 /* ok */
592 lsocket = sfd;
593 if (verbose > 0)
594 fprintf (stderr, "%s: listening to port %d on fd %d\n",
595 indi_tstamp(NULL), port, sfd);
596}
597
598/* Attempt to open up FIFO */
599static void indiFIFO(void)
600{
601 /* Open up FIFO, if available */
602 if (fifo.name)
603 {
604 fifo.fd = open(fifo.name, O_RDONLY);
605 if (fifo.fd < 0)
606 {
607 fprintf(stderr, "%s: open(%s): %s.\n", indi_tstamp(NULL), fifo.name, strerror(errno));
608 Bye();
609 }
610
611 fifo.fs = fdopen(fifo.fd, "r");
612 }
613}
614
615/* service traffic from clients and drivers */
616static void
617indiRun(void)
618{
619 fd_set rs, ws;
620 int maxfd=0;
621 int i, s;
622
623 /* init with no writers or readers */
624 FD_ZERO(&ws);
625 FD_ZERO(&rs);
626
627 if (fifo.name && fifo.fd > 0)
628 {
629 FD_SET(fifo.fd, &rs);
630 maxfd = fifo.fd;
631 }
632
633 /* always listen for new clients */
634 FD_SET(lsocket, &rs);
635 if (lsocket > maxfd)
636 maxfd = lsocket;
637
638 /* add all client readers and client writers with work to send */
639 for (i = 0; i < nclinfo; i++) {
640 ClInfo *cp = &clinfo[i];
641 if (cp->active) {
642 FD_SET(cp->s, &rs);
643 if (nFQ(cp->msgq) > 0)
644 FD_SET(cp->s, &ws);
645 if (cp->s > maxfd)
646 maxfd = cp->s;
647 }
648 }
649
650 /* add all driver readers and driver writers with work to send */
651 for (i = 0; i < ndvrinfo; i++)
652 {
653 DvrInfo *dp = &dvrinfo[i];
654 if (dp->active)
655 {
656 FD_SET(dp->rfd, &rs);
657 if (dp->rfd > maxfd)
658 maxfd = dp->rfd;
659 if (dp->pid != REMOTEDVR)
660 {
661 FD_SET(dp->efd, &rs);
662 if (dp->efd > maxfd)
663 maxfd = dp->efd;
664 }
665 if (nFQ(dp->msgq) > 0)
666 {
667 FD_SET(dp->wfd, &ws);
668 if (dp->wfd > maxfd)
669 maxfd = dp->wfd;
670 }
671 }
672 }
673
674 /* wait for action */
675 s = select (maxfd+1, &rs, &ws, NULL, NULL);
676 if (s < 0) {
677 fprintf (stderr, "%s: select(%d): %s\n", indi_tstamp(NULL), maxfd+1,
678 strerror(errno));
679 Bye();
680 }
681
682
683 /* new command from FIFO? */
684 if (s > 0 && FD_ISSET(fifo.fd, &rs)) {
685 newFIFO();
686 s--;
687 }
688
689 /* new client? */
690 if (s > 0 && FD_ISSET(lsocket, &rs)) {
691 newClient();
692 s--;
693 }
694
695 /* message to/from client? */
696 for (i = 0; s > 0 && i < nclinfo; i++) {
697 ClInfo *cp = &clinfo[i];
698 if (cp->active) {
699 if (FD_ISSET(cp->s, &rs)) {
700 if (readFromClient(cp) < 0)
701 return; /* fds effected */
702 s--;
703 }
704 if (s > 0 && FD_ISSET(cp->s, &ws)) {
705 if (sendClientMsg(cp) < 0)
706 return; /* fds effected */
707 s--;
708 }
709 }
710 }
711
712 /* message to/from driver? */
713 for (i = 0; s > 0 && i < ndvrinfo; i++) {
714 DvrInfo *dp = &dvrinfo[i];
715 if (dp->pid != REMOTEDVR && FD_ISSET(dp->efd, &rs)) {
716 if (stderrFromDriver(dp) < 0)
717 return; /* fds effected */
718 s--;
719 }
720 if (s > 0 && FD_ISSET(dp->rfd, &rs)) {
721 if (readFromDriver(dp) < 0)
722 return; /* fds effected */
723 s--;
724 }
725 if (s > 0 && FD_ISSET(dp->wfd, &ws) && nFQ(dp->msgq) > 0) {
726 if (sendDriverMsg(dp) < 0)
727 return; /* fds effected */
728 s--;
729 }
730 }
731}
732
733/* Read commands from FIFO and process them. Start/stop drivers accordingly */
734static void newFIFO(void)
735{
736 char line[MAXRBUF], tDriver[MAXRBUF], tConfig[MAXRBUF], tDev[MAXRBUF];
737 static char *envDev=NULL, *envConfig=NULL;
738 const char *delm = " ";
739 char *token, *cp;
740 DvrInfo *dp = NULL;
741 int startCmd=0;
742
743 if (envDev == NULL)
744 envDev = (char *) malloc(MAXRBUF* sizeof(char));
745 if (envConfig == NULL)
746 envConfig = (char *) malloc(MAXRBUF * sizeof(char));
747
748 while ( fgets (line, MAXRBUF, fifo.fs) != NULL)
749 {
750 //fprintf(stderr, "FIFO: %s\n", line);
751 tDev[0] = '\0', tDriver[0] = '\0', tConfig[0] = '\0', envDev[0] = '\0', envConfig[0] = '\0';
752 cp = strdup(line);
753
754 token = strsep(&cp, delm);
755
756 if (!strcmp(token, "start") || !strcmp(token, "stop"))
757 {
758 if (!strcmp(token, "start"))
759 startCmd = 1;
760
761 token = strsep(&cp, delm);
762
763 if (!token)
764 continue;
765
766 strncpy(tDriver, token, MAXRBUF);
767
768 if (verbose)
769 fprintf(stderr, "FIFO: Request for %s driver: %s\n", (startCmd == 1) ? "starting" : "stopping", tDriver);
770
771 /* Find more name + config */
772 token = strsep(&cp, delm);
773
774 if (token)
775 {
776 /* If config file detected, copy it */
777 if (strstr(token, ".xml"))
778 strncpy(tConfig, token, MAXRBUF);
779 /* Get rid of quotes */
780 else if (strstr(token, "\"") || strstr(token, "'"))
781 {
782 strncat(tDev, ++token, MAXRBUF);
783 while (token = strsep(&cp, delm) )
784 {
785 strcat(tDev, " ");
786 strncat(tDev, token, MAXRBUF);
787 if (strstr(token, "\"") || strstr(token, "'"))
788 {
789 tDev[strlen(tDev)-1] = '\0';
790 break;
791 }
792 }
793 }
794 else
795 strncpy(tDev, token, MAXRBUF);
796
797 /* Find config, if there is any */
798 token = strsep(&cp, delm);
799 if (token)
800 {
801 /* Get rid of quotes */
802 if (strstr(token, "\"") || strstr(token, "'"))
803 {
804 strncat(tConfig, ++token, MAXRBUF);
805 while (token = strsep(&cp, delm) )
806 {
807 strcat(tConfig, " ");
808 strncat(tConfig, token, MAXRBUF);
809
810 if (strstr(token, "\"") || strstr(token, "'"))
811 {
812 tConfig[strlen(tConfig)-1] = '\0';
813 break;
814 }
815 }
816 }
817 else
818 strncpy(tConfig, token, MAXRBUF);
819 }
820 }
821
822 /* Did we find device name? */
823 if (tDev[0])
824 {
825 snprintf(envDev, MAXRBUF, "INDIDEV=%s", tDev);
826 //fprintf(stderr, "With name: %s\n", envDev);
827 putenv(envDev);
828
829 //fprintf(stderr, "envionment check INDIDEV: %s\n", getenv("INDIDEV"));
830 }
831
832 /* Did we find config file */
833 if (tConfig[0])
834 {
835 snprintf(envConfig, MAXRBUF, "INDICONFIG=%s", tConfig);
836 //fprintf(stderr, "With config: %s\n", envConfig);
837 putenv(envConfig);
838 }
839
840 if (startCmd)
841 {
842 if (verbose)
843 fprintf(stderr, "FIFO: Starting driver %s\n", tDriver);
844 dp = allocDvr();
845 strncpy(dp->name, tDriver, MAXINDIDEVICE);
846 strncpy(dp->dev, tDev, MAXINDIDEVICE);
847 startDvr (dp);
848 }
849 else
850 {
851 for (dp = dvrinfo; dp < &dvrinfo[ndvrinfo]; dp++)
852 {
853 if (!strcmp(dp->name, tDriver))
854 {
855 /* If device name is given, check against it before shutting down */
856 if (tDev[0] && strcmp(dp->dev, tDev))
857 continue;
858
859 if (verbose)
860 fprintf(stderr, "FIFO: Shutting down driver: %s\n", tDriver);
861
862 shutdownDvr(dp, 0);
863
864 /* Inform clients that this driver is dead */
865 XMLEle *root = addXMLEle (NULL, "delProperty");
866 addXMLAtt(root, "device", dp->dev);
867 Msg * mp = newMsg();
868
869 q2Clients(NULL, 0, dp->dev, NULL, mp, root);
870 if (mp->count > 0)
871 setMsgXMLEle (mp, root);
872 else
873 freeMsg (mp);
874 delXMLEle (root);
875
876 break;
877
878 }
879 }
880 }
881 }
882 }
883}
884
885/* prepare for new client arriving on lsocket.
886 * exit if trouble.
887 */
888static void
889newClient()
890{
891 ClInfo *cp = NULL;
892 int s, cli;
893
894 /* assign new socket */
895 s = newClSocket ();
896
897 /* try to reuse a clinfo slot, else add one */
898 for (cli = 0; cli < nclinfo; cli++)
899 if (!(cp = &clinfo[cli])->active)
900 break;
901 if (cli == nclinfo) {
902 /* grow clinfo */
903 clinfo = (ClInfo *) realloc (clinfo, (nclinfo+1)*sizeof(ClInfo));
904 if (!clinfo) {
905 fprintf (stderr, "no memory for new client\n");
906 Bye();
907 }
908 cp = &clinfo[nclinfo++];
909 }
910
911 /* rig up new clinfo entry */
912 memset (cp, 0, sizeof(*cp));
913 cp->active = 1;
914 cp->s = s;
915 cp->lp = newLilXML();
916 cp->msgq = newFQ(1);
917 cp->props = malloc (1);
918 cp->nsent = 0;
919
920 if (verbose > 0) {
921 struct sockaddr_in addr;
922 socklen_t len = sizeof(addr);
923 getpeername(s, (struct sockaddr*)&addr, &len);
924 fprintf(stderr,"%s: Client %d: new arrival from %s:%d - welcome!\n",
925 indi_tstamp(NULL), cp->s, inet_ntoa(addr.sin_addr),
926 ntohs(addr.sin_port));
927 }
928}
929
930/* read more from the given client, send to each appropriate driver when see
931 * xml closure. also send all newXXX() to all other interested clients.
932 * return -1 if had to shut down anything, else 0.
933 */
934static int
935readFromClient (ClInfo *cp)
936{
937 char buf[MAXRBUF];
938 int shutany = 0;
939 int i, nr;
940
941 /* read client */
942 nr = read (cp->s, buf, sizeof(buf));
943 if (nr <= 0) {
944 if (nr < 0)
945 fprintf (stderr, "%s: Client %d: read: %s\n", indi_tstamp(NULL),
946 cp->s, strerror(errno));
947 else if (verbose > 0)
948 fprintf (stderr, "%s: Client %d: read EOF\n", indi_tstamp(NULL),
949 cp->s);
950 shutdownClient (cp);
951 return (-1);
952 }
953
954 /* process XML, sending when find closure */
955 for (i = 0; i < nr; i++) {
956 char err[1024];
957 XMLEle *root = readXMLEle (cp->lp, buf[i], err);
958 if (root) {
959 char *roottag = tagXMLEle(root);
960 const char *dev = findXMLAttValu (root, "device");
961 const char *name = findXMLAttValu (root, "name");
962 int isblob = !strcmp (tagXMLEle(root), "setBLOBVector");
963 Msg *mp;
964
965 if (verbose > 2) {
966 fprintf (stderr, "%s: Client %d: read ",indi_tstamp(NULL),cp->s);
967 traceMsg (root);
968 } else if (verbose > 1) {
969 fprintf (stderr, "%s: Client %d: read <%s device='%s' name='%s'>\n",
970 indi_tstamp(NULL), cp->s, tagXMLEle(root),
971 findXMLAttValu (root, "device"),
972 findXMLAttValu (root, "name"));
973 }
974
975 /* snag interested properties.
976 * N.B. don't open to alldevs if seen specific dev already, else
977 * remote client connections start returning too much.
978 */
979 if (dev[0])
980 addClDevice (cp, dev, name);
981 else if (!strcmp (roottag, "getProperties") && !cp->nprops)
982 cp->allprops = 1;
983
984 /* snag enableBLOB -- send to remote drivers too */
985 if (!strcmp (roottag, "enableBLOB"))
986 crackBLOB (pcdataXMLEle(root), &cp->blob);
987
988 /* build a new message -- set content iff anyone cares */
989 mp = newMsg();
990
991 /* send message to driver(s) responsible for dev */
992 q2RDrivers (dev, mp, root);
993
994 /* echo new* commands back to other clients */
995 if (!strncmp (roottag, "new", 3)) {
996 if (q2Clients (cp, isblob, dev, name, mp, root) < 0)
997 shutany++;
998 }
999
1000 /* set message content if anyone cares else forget it */
1001 if (mp->count > 0)
1002 setMsgXMLEle (mp, root);
1003 else
1004 freeMsg (mp);
1005 delXMLEle (root);
1006
1007 } else if (err[0]) {
1008 char *ts = indi_tstamp(NULL);
1009 fprintf (stderr, "%s: Client %d: XML error: %s\n", ts,
1010 cp->s, err);
1011 fprintf (stderr, "%s: Client %d: XML read: %.*s\n", ts,
1012 cp->s, nr, buf);
1013 shutdownClient (cp);
1014 return (-1);
1015 }
1016 }
1017
1018 return (shutany ? -1 : 0);
1019}
1020
1021/* read more from the given driver, send to each interested client when see
1022 * xml closure. if driver dies, try restarting.
1023 * return 0 if ok else -1 if had to shut down anything.
1024 */
1025static int
1026readFromDriver (DvrInfo *dp)
1027{
1028 char buf[MAXRBUF];
1029 int shutany = 0;
1030 int i, nr;
1031
1032 /* read driver */
1033 nr = read (dp->rfd, buf, sizeof(buf));
1034 if (nr <= 0) {
1035 if (nr < 0)
1036 fprintf (stderr, "%s: Driver %s: stdin %s\n", indi_tstamp(NULL),
1037 dp->name, strerror(errno));
1038 else
1039 fprintf (stderr, "%s: Driver %s: stdin EOF\n",
1040 indi_tstamp(NULL), dp->name);
1041 shutdownDvr (dp, 1);
1042 return (-1);
1043 }
1044
1045 /* process XML, sending when find closure */
1046 for (i = 0; i < nr; i++) {
1047 char err[1024];
1048 XMLEle *root = readXMLEle (dp->lp, buf[i], err);
1049 if (root) {
1050 char *roottag = tagXMLEle(root);
1051 const char *dev = findXMLAttValu (root, "device");
1052 const char *name = findXMLAttValu (root, "name");
1053 int isblob = !strcmp (tagXMLEle(root), "setBLOBVector");
1054 Msg *mp;
1055
1056 if (verbose > 2) {
1057 fprintf(stderr, "%s: Driver %s: read ", indi_tstamp(0),dp->name);
1058 traceMsg (root);
1059 } else if (verbose > 1) {
1060 fprintf (stderr, "%s: Driver %s: read <%s device='%s' name='%s'>\n",
1061 indi_tstamp(NULL), dp->name, tagXMLEle(root),
1062 findXMLAttValu (root, "device"),
1063 findXMLAttValu (root, "name"));
1064 }
1065
1066 /* that's all if driver is just registering a snoop */
1067 if (!strcmp (roottag, "getProperties")) {
1068 addSDevice (dp, dev, name);
1069 delXMLEle (root);
1070 continue;
1071 }
1072
1073 /* that's all if driver is just registering a BLOB mode */
1074 if (!strcmp (roottag, "enableBLOB")) {
1075 Snoopee *sp = findSDevice (dp, dev, name);
1076 if (sp)
1077 crackBLOB (pcdataXMLEle (root), &sp->blob);
1078 delXMLEle (root);
1079 continue;
1080 }
1081
1082 /* snag device name if not known yet */
1083 if (!dp->dev[0] && dev[0]) {
1084 strncpy (dp->dev, dev, MAXINDIDEVICE-1);
1085 dp->dev[MAXINDIDEVICE-1] = '\0';
1086 }
1087
1088 /* log messages if any and wanted */
1089 if (ldir)
1090 logDMsg (root, dev);
1091
1092 /* build a new message -- set content iff anyone cares */
1093 mp = newMsg();
1094
1095 /* send to interested clients */
1096 if (q2Clients (NULL, isblob, dev, name, mp, root) < 0)
1097 shutany++;
1098
1099 /* send to snooping drivers */
1100 q2SDrivers (isblob, dev, name, mp, root);
1101
1102 /* set message content if anyone cares else forget it */
1103 if (mp->count > 0)
1104 setMsgXMLEle (mp, root);
1105 else
1106 freeMsg (mp);
1107 delXMLEle (root);
1108
1109 } else if (err[0]) {
1110 char *ts = indi_tstamp(NULL);
1111 fprintf (stderr, "%s: Driver %s: XML error: %s\n", ts,
1112 dp->name, err);
1113 fprintf (stderr, "%s: Driver %s: XML read: %.*s\n", ts,
1114 dp->name, nr, buf);
1115 shutdownDvr (dp, 1);
1116 return (-1);
1117 }
1118 }
1119
1120 return (shutany ? -1 : 0);
1121}
1122
1123/* read more from the given driver stderr, add prefix and send to our stderr.
1124 * return 0 if ok else -1 if had to restart.
1125 */
1126static int
1127stderrFromDriver (DvrInfo *dp)
1128{
1129 static char exbuf[MAXRBUF];
1130 static int nexbuf;
1131 int i, nr;
1132
1133 /* read more */
1134 nr = read (dp->efd, exbuf+nexbuf, sizeof(exbuf)-nexbuf);
1135 if (nr <= 0) {
1136 if (nr < 0)
1137 fprintf (stderr, "%s: Driver %s: stderr %s\n", indi_tstamp(NULL),
1138 dp->name, strerror(errno));
1139 else
1140 fprintf (stderr, "%s: Driver %s: stderr EOF\n",
1141 indi_tstamp(NULL), dp->name);
1142 shutdownDvr (dp, 1);
1143 return (-1);
1144 }
1145 nexbuf += nr;
1146
1147 /* prefix each whole line to our stderr, save extra for next time */
1148 for (i = 0; i < nexbuf; i++) {
1149 if (exbuf[i] == '\n') {
1150 fprintf (stderr, "%s: Driver %s: %.*s\n", indi_tstamp(NULL),
1151 dp->name, i, exbuf);
1152 i++; /* count including nl */
1153 nexbuf -= i; /* remove from nexbuf */
1154 memmove (exbuf, exbuf+i, nexbuf); /* slide remaining to front */
1155 i = -1; /* restart for loop scan */
1156 }
1157 }
1158
1159 return (0);
1160}
1161
1162/* close down the given client */
1163static void
1164shutdownClient (ClInfo *cp)
1165{
1166 Msg *mp;
1167
1168 /* close connection */
1169 shutdown (cp->s, SHUT_RDWR);
1170 close (cp->s);
1171
1172 /* free memory */
1173 delLilXML (cp->lp);
1174 free (cp->props);
1175
1176 /* decrement and possibly free any unsent messages for this client */
1177 while ((mp = (Msg*) popFQ(cp->msgq)) != NULL)
1178 if (--mp->count == 0)
1179 freeMsg (mp);
1180 delFQ (cp->msgq);
1181
1182 /* ok now to recycle */
1183 cp->active = 0;
1184
1185 if (verbose > 0)
1186 fprintf (stderr, "%s: Client %d: shut down complete - bye!\n",
1187 indi_tstamp(NULL), cp->s);
1188}
1189
1190/* close down the given driver and restart */
1191static void
1192shutdownDvr (DvrInfo *dp, int restart)
1193{
1194 Msg *mp;
1195
1196 /* make sure it's dead, reclaim resources */
1197 if (dp->pid == REMOTEDVR) {
1198 /* socket connection */
1199 shutdown (dp->wfd, SHUT_RDWR);
1200 close (dp->wfd); /* same as rfd */
1201 } else {
1202 /* local pipe connection */
1203 kill (dp->pid, SIGKILL); /* we've insured there are no zombies */
1204 close (dp->wfd);
1205 close (dp->rfd);
1206 close (dp->efd);
1207 }
1208
1209 /* free memory */
1210 free (dp->sprops);
1211 delLilXML (dp->lp);
1212
1213 /* ok now to recycle */
1214 dp->active = 0;
1215
1216 /* decrement and possibly free any unsent messages for this client */
1217 while ((mp = (Msg*) popFQ(dp->msgq)) != NULL)
1218 if (--mp->count == 0)
1219 freeMsg (mp);
1220 delFQ (dp->msgq);
1221
1222 if (restart)
1223 {
1224 fprintf (stderr, "%s: Driver %s: restart #%d\n", indi_tstamp(NULL),
1225 dp->name, ++dp->restarts);
1226 startDvr (dp);
1227 }
1228
1229}
1230
1231/* put Msg mp on queue of each driver responsible for dev, or all drivers
1232 * if dev not specified.
1233 */
1234static void
1235q2RDrivers (const char *dev, Msg *mp, XMLEle *root)
1236{
1237 int sawremote = 0;
1238 DvrInfo *dp;
1239
1240 /* queue message to each interested driver.
1241 * N.B. don't send generic getProps to more than one remote driver,
1242 * otherwise they all fan out and we get multiple responses back.
1243 */
1244 for (dp = dvrinfo; dp < &dvrinfo[ndvrinfo]; dp++) {
1245 int isremote = (dp->pid == REMOTEDVR);
1246 if (dev[0] && dp->dev[0] && strcmp (dev, dp->dev))
1247 continue; /* driver known to not support this dev */
1248 if (!dev[0] && isremote && sawremote)
1249 continue; /* already sent generic to another remote */
1250 if (isremote)
1251 sawremote = 1;
1252
1253 /* ok: queue message to this driver */
1254 mp->count++;
1255 pushFQ (dp->msgq, mp);
1256 if (verbose > 1)
1257 fprintf (stderr, "%s: Driver %s: queuing responsible for <%s device='%s' name='%s'>\n",
1258 indi_tstamp(NULL), dp->name, tagXMLEle(root),
1259 findXMLAttValu (root, "device"),
1260 findXMLAttValu (root, "name"));
1261 }
1262}
1263
1264/* put Msg mp on queue of each driver snooping dev/name.
1265 * if BLOB always honor current mode.
1266 */
1267static void
1268q2SDrivers (int isblob, const char *dev, const char *name, Msg *mp, XMLEle *root)
1269{
1270 DvrInfo *dp;
1271
1272 for (dp = dvrinfo; dp < &dvrinfo[ndvrinfo]; dp++) {
1273 Snoopee *sp = findSDevice (dp, dev, name);
1274
1275 /* nothing for dp if not snooping for dev/name or wrong BLOB mode */
1276 if (!sp)
1277 continue;
1278 if ((isblob && sp->blob==B_NEVER) || (!isblob && sp->blob==B_ONLY))
1279 continue;
1280
1281 /* ok: queue message to this device */
1282 mp->count++;
1283 pushFQ (dp->msgq, mp);
1284 if (verbose > 1) {
1285 fprintf (stderr, "%s: Driver %s: queuing snooped <%s device='%s' name='%s'>\n",
1286 indi_tstamp(NULL), dp->name, tagXMLEle(root),
1287 findXMLAttValu (root, "device"),
1288 findXMLAttValu (root, "name"));
1289 }
1290 }
1291}
1292
1293/* add dev/name to dp's snooping list.
1294 * init with blob mode set to B_NEVER.
1295 */
1296static void
1297addSDevice (DvrInfo *dp, const char *dev, const char *name)
1298{
1299 Snoopee *sp;
1300 char *ip;
1301
1302 /* no dups */
1303 sp = findSDevice (dp, dev, name);
1304 if (sp)
1305 return;
1306
1307 /* add dev to sdevs list */
1308 dp->sprops = (Snoopee*) realloc (dp->sprops,
1309 (dp->nsprops+1)*sizeof(Snoopee));
1310 sp = &dp->sprops[dp->nsprops++];
1311
1312 ip = sp->prop.dev;
1313 strncpy (ip, dev, MAXINDIDEVICE-1);
1314 ip[MAXINDIDEVICE-1] = '\0';
1315
1316 ip = sp->prop.name;
1317 strncpy (ip, name, MAXINDINAME-1);
1318 ip[MAXINDINAME-1] = '\0';
1319
1320 sp->blob = B_NEVER;
1321
1322 if (verbose)
1323 fprintf (stderr, "%s: Driver %s: snooping on %s.%s\n", indi_tstamp(NULL),
1324 dp->name, dev, name);
1325}
1326
1327/* return Snoopee if dp is snooping dev/name, else NULL.
1328 */
1329static Snoopee *
1330findSDevice (DvrInfo *dp, const char *dev, const char *name)
1331{
1332 int i;
1333
1334 for (i = 0; i < dp->nsprops; i++) {
1335 Snoopee *sp = &dp->sprops[i];
1336 Property *pp = &sp->prop;
1337 if (!strcmp (pp->dev, dev) &&
1338 (!pp->name[0] || !strcmp(pp->name, name)))
1339 return (sp);
1340 }
1341
1342 return (NULL);
1343}
1344
1345/* put Msg mp on queue of each client interested in dev/name, except notme.
1346 * if BLOB always honor current mode.
1347 * return -1 if had to shut down any clients, else 0.
1348 */
1349static int
1350q2Clients (ClInfo *notme, int isblob, const char *dev, const char *name, Msg *mp, XMLEle *root)
1351{
1352 int shutany = 0;
1353 ClInfo *cp;
1354 int ql;
1355
1356 /* queue message to each interested client */
1357 for (cp = clinfo; cp < &clinfo[nclinfo]; cp++) {
1358 /* cp in use? notme? want this dev/name? blob? */
1359 if (!cp->active || cp == notme)
1360 continue;
1361 if (findClDevice (cp, dev, name) < 0)
1362 continue;
1363 if ((isblob && cp->blob==B_NEVER) || (!isblob && cp->blob==B_ONLY))
1364 continue;
1365
1366 /* shut down this client if its q is already too large */
1367 ql = msgQSize(cp->msgq);
1368 if (ql > maxqsiz) {
1369 if (verbose)
1370 fprintf (stderr, "%s: Client %d: %d bytes behind, shutting down\n",
1371 indi_tstamp(NULL), cp->s, ql);
1372 shutdownClient (cp);
1373 shutany++;
1374 continue;
1375 }
1376
1377 /* ok: queue message to this client */
1378 mp->count++;
1379 pushFQ (cp->msgq, mp);
1380 if (verbose > 1)
1381 fprintf (stderr, "%s: Client %d: queuing <%s device='%s' name='%s'>\n",
1382 indi_tstamp(NULL), cp->s, tagXMLEle(root),
1383 findXMLAttValu (root, "device"),
1384 findXMLAttValu (root, "name"));
1385 }
1386
1387 return (shutany ? -1 : 0);
1388}
1389
1390/* return size of all Msqs on the given q */
1391static int
1392msgQSize (FQ *q)
1393{
1394 int i, l = 0;
1395
1396 for (i = 0; i < nFQ(q); i++) {
1397 Msg *mp = (Msg *) peekiFQ(q,i);
1398 l += mp->cl;
1399 }
1400
1401 return (l);
1402}
1403
1404/* print root as content in Msg mp.
1405 */
1406static void
1407setMsgXMLEle (Msg *mp, XMLEle *root)
1408{
1409 /* want cl to only count content, but need room for final \0 */
1410 mp->cl = sprlXMLEle (root, 0);
1411 if (mp->cl < sizeof(mp->buf))
1412 mp->cp = mp->buf;
1413 else
1414 mp->cp = malloc (mp->cl+1);
1415 sprXMLEle (mp->cp, root, 0);
1416}
1417
1418/* save str as content in Msg mp.
1419 */
1420static void
1421setMsgStr (Msg *mp, char *str)
1422{
1423 /* want cl to only count content, but need room for final \0 */
1424 mp->cl = strlen (str);
1425 if (mp->cl < sizeof(mp->buf))
1426 mp->cp = mp->buf;
1427 else
1428 mp->cp = malloc (mp->cl+1);
1429 strcpy (mp->cp, str);
1430}
1431
1432/* return pointer to one new nulled Msg
1433 */
1434static Msg *
1435newMsg (void)
1436{
1437 return ((Msg *) calloc (1, sizeof(Msg)));
1438}
1439
1440/* free Msg mp and everything it contains */
1441static void
1442freeMsg (Msg *mp)
1443{
1444 if (mp->cp && mp->cp != mp->buf)
1445 free (mp->cp);
1446 free (mp);
1447}
1448
1449/* write the next chunk of the current message in the queue to the given
1450 * client. pop message from queue when complete and free the message if we are
1451 * the last one to use it. shut down this client if trouble.
1452 * N.B. we assume we will never be called with cp->msgq empty.
1453 * return 0 if ok else -1 if had to shut down.
1454 */
1455static int
1456sendClientMsg (ClInfo *cp)
1457{
1458 int nsend, nw;
1459 Msg *mp;
1460
1461 /* get current message */
1462 mp = (Msg *) peekFQ (cp->msgq);
1463
1464 /* send next chunk, never more than MAXWSIZ to reduce blocking */
1465 nsend = mp->cl - cp->nsent;
1466 if (nsend > MAXWSIZ)
1467 nsend = MAXWSIZ;
1468 nw = write (cp->s, &mp->cp[cp->nsent], nsend);
1469
1470 /* shut down if trouble */
1471 if (nw <= 0) {
1472 if (nw == 0)
1473 fprintf (stderr, "%s: Client %d: write returned 0\n",
1474 indi_tstamp(NULL), cp->s);
1475 else
1476 fprintf (stderr, "%s: Client %d: write: %s\n", indi_tstamp(NULL),
1477 cp->s, strerror(errno));
1478 shutdownClient (cp);
1479 return (-1);
1480 }
1481
1482 /* trace */
1483 if (verbose > 2) {
1484 fprintf(stderr, "%s: Client %d: sending msg copy %d nq %d:\n%.*s\n",
1485 indi_tstamp(NULL), cp->s, mp->count, nFQ(cp->msgq),
1486 nw, &mp->cp[cp->nsent]);
1487 } else if (verbose > 1) {
1488 fprintf(stderr, "%s: Client %d: sending %.50s\n", indi_tstamp(NULL),
1489 cp->s, &mp->cp[cp->nsent]);
1490 }
1491
1492 /* update amount sent. when complete: free message if we are the last
1493 * to use it and pop from our queue.
1494 */
1495 cp->nsent += nw;
1496 if (cp->nsent == mp->cl) {
1497 if (--mp->count == 0)
1498 freeMsg (mp);
1499 popFQ (cp->msgq);
1500 cp->nsent = 0;
1501 }
1502
1503 return (0);
1504}
1505
1506/* write the next chunk of the current message in the queue to the given
1507 * driver. pop message from queue when complete and free the message if we are
1508 * the last one to use it. restart this driver if touble.
1509 * N.B. we assume we will never be called with dp->msgq empty.
1510 * return 0 if ok else -1 if had to shut down.
1511 */
1512static int
1513sendDriverMsg (DvrInfo *dp)
1514{
1515 int nsend, nw;
1516 Msg *mp;
1517
1518 /* get current message */
1519 mp = (Msg *) peekFQ (dp->msgq);
1520
1521 /* send next chunk, never more than MAXWSIZ to reduce blocking */
1522 nsend = mp->cl - dp->nsent;
1523 if (nsend > MAXWSIZ)
1524 nsend = MAXWSIZ;
1525 nw = write (dp->wfd, &mp->cp[dp->nsent], nsend);
1526
1527 /* restart if trouble */
1528 if (nw <= 0) {
1529 if (nw == 0)
1530 fprintf (stderr, "%s: Driver %s: write returned 0\n",
1531 indi_tstamp(NULL), dp->name);
1532 else
1533 fprintf (stderr, "%s: Driver %s: write: %s\n", indi_tstamp(NULL),
1534 dp->name, strerror(errno));
1535 shutdownDvr (dp, 1);
1536 return (-1);
1537 }
1538
1539 /* trace */
1540 if (verbose > 2) {
1541 fprintf(stderr, "%s: Driver %s: sending msg copy %d nq %d:\n%.*s\n",
1542 indi_tstamp(NULL), dp->name, mp->count, nFQ(dp->msgq),
1543 nw, &mp->cp[dp->nsent]);
1544 } else if (verbose > 1) {
1545 fprintf(stderr, "%s: Driver %s: sending %.50s\n", indi_tstamp(NULL),
1546 dp->name, &mp->cp[dp->nsent]);
1547 }
1548
1549 /* update amount sent. when complete: free message if we are the last
1550 * to use it and pop from our queue.
1551 */
1552 dp->nsent += nw;
1553 if (dp->nsent == mp->cl) {
1554 if (--mp->count == 0)
1555 freeMsg (mp);
1556 popFQ (dp->msgq);
1557 dp->nsent = 0;
1558 }
1559
1560 return (0);
1561}
1562
1563/* return 0 if cp may be interested in dev/name else -1
1564 */
1565static int
1566findClDevice (ClInfo *cp, const char *dev, const char *name)
1567{
1568 int i;
1569
1570 if (cp->allprops || !dev[0])
1571 return (0);
1572 for (i = 0; i < cp->nprops; i++) {
1573 Property *pp = &cp->props[i];
1574 if (!strcmp (pp->dev, dev) &&
1575 (!pp->name[0] || !strcmp(pp->name, name)))
1576 return (0);
1577 }
1578 return (-1);
1579}
1580
1581/* add the given device and property to the devs[] list of client if new.
1582 */
1583static void
1584addClDevice (ClInfo *cp, const char *dev, const char *name)
1585{
1586 Property *pp;
1587 char *ip;
1588
1589 /* no dups */
1590 if (!findClDevice (cp, dev, name))
1591 return;
1592
1593 /* add */
1594 cp->props = (Property *) realloc (cp->props,
1595 (cp->nprops+1)*sizeof(Property));
1596 pp = &cp->props[cp->nprops++];
1597
1598 ip = pp->dev;
1599 strncpy (ip, dev, MAXINDIDEVICE-1);
1600 ip[MAXINDIDEVICE-1] = '\0';
1601
1602 ip = pp->name;
1603 strncpy (ip, name, MAXINDINAME-1);
1604 ip[MAXINDINAME-1] = '\0';
1605}
1606
1607
1608/* block to accept a new client arriving on lsocket.
1609 * return private nonblocking socket or exit.
1610 */
1611static int
1612newClSocket ()
1613{
1614 struct sockaddr_in cli_socket;
1615 socklen_t cli_len;
1616 int cli_fd;
1617
1618 /* get a private connection to new client */
1619 cli_len = sizeof(cli_socket);
1620 cli_fd = accept (lsocket, (struct sockaddr *)&cli_socket, &cli_len);
1621 if(cli_fd < 0) {
1622 fprintf (stderr, "accept: %s\n", strerror(errno));
1623 Bye();
1624 }
1625
1626 /* ok */
1627 return (cli_fd);
1628}
1629
1630/* convert the string value of enableBLOB to our B_ state value.
1631 * no change if unrecognized
1632 */
1633static void
1634crackBLOB (char *enableBLOB, BLOBHandling *bp)
1635{
1636 if (!strcmp (enableBLOB, "Also"))
1637 *bp = B_ALSO;
1638 else if (!strcmp (enableBLOB, "Only"))
1639 *bp = B_ONLY;
1640 else if (!strcmp (enableBLOB, "Never"))
1641 *bp = B_NEVER;
1642}
1643
1644/* print key attributes and values of the given xml to stderr.
1645 */
1646static void
1647traceMsg (XMLEle *root)
1648{
1649 static const char *prtags[] = {
1650 "defNumber", "oneNumber",
1651 "defText", "oneText",
1652 "defSwitch", "oneSwitch",
1653 "defLight", "oneLight",
1654 };
1655 XMLEle *e;
1656 const char *msg, *perm, *pcd;
1657 unsigned int i;
1658
1659 /* print tag header */
1660 fprintf (stderr, "%s %s %s %s", tagXMLEle(root),
1661 findXMLAttValu(root,"device"),
1662 findXMLAttValu(root,"name"),
1663 findXMLAttValu(root,"state"));
1664 pcd = pcdataXMLEle (root);
1665 if (pcd[0])
1666 fprintf (stderr, " %s", pcd);
1667 perm = findXMLAttValu(root,"perm");
1668 if (perm[0])
1669 fprintf (stderr, " %s", perm);
1670 msg = findXMLAttValu(root,"message");
1671 if (msg[0])
1672 fprintf (stderr, " '%s'", msg);
1673
1674 /* print each array value */
1675 for (e = nextXMLEle(root,1); e; e = nextXMLEle(root,0))
1676 for (i = 0; i < sizeof(prtags)/sizeof(prtags[0]); i++)
1677 if (strcmp (prtags[i], tagXMLEle(e)) == 0)
1678 fprintf (stderr, "\n %10s='%s'", findXMLAttValu(e,"name"),
1679 pcdataXMLEle (e));
1680
1681 fprintf (stderr, "\n");
1682}
1683
1684/* fill s with current UT string.
1685 * if no s, use a static buffer
1686 * return s or buffer.
1687 * N.B. if use our buffer, be sure to use before calling again
1688 */
1689static char *
1690indi_tstamp (char *s)
1691{
1692 static char sbuf[64];
1693 struct tm *tp;
1694 time_t t;
1695
1696 time (&t);
1697 tp = gmtime (&t);
1698 if (!s)
1699 s = sbuf;
1700 strftime (s, sizeof(sbuf), "%Y-%m-%dT%H:%M:%S", tp);
1701 return (s);
1702}
1703
1704/* log message in root known to be from device dev to ldir, if any.
1705 */
1706static void
1707logDMsg (XMLEle *root, const char *dev)
1708{
1709 char stamp[64];
1710 char logfn[1024];
1711 const char *ts, *ms;
1712 FILE *fp;
1713
1714 /* get message, if any */
1715 ms = findXMLAttValu (root, "message");
1716 if (!ms[0])
1717 return;
1718
1719 /* get timestamp now if not provided */
1720 ts = findXMLAttValu (root, "timestamp");
1721 if (!ts[0])
1722 {
1723 indi_tstamp (stamp);
1724 ts = stamp;
1725 }
1726
1727 /* append to log file, name is date portion of time stamp */
1728 sprintf (logfn, "%s/%.10s.islog", ldir, ts);
1729 fp = fopen (logfn, "a");
1730 if (!fp)
1731 return; /* oh well */
1732 fprintf (fp, "%s: %s: %s\n", ts, dev, ms);
1733 fclose (fp);
1734}
1735
1736/* log when then exit */
1737static void
1738Bye()
1739{
1740 fprintf (stderr, "%s: good bye\n", indi_tstamp(NULL));
1741 exit(1);
1742}
1743
Note: See TracBrowser for help on using the repository browser.