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

Last change on this file since 693 was 501, checked in by frichard, 14 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.