source: BAORadio/libindi/libindi/indiserver.c @ 629

Last change on this file since 629 was 504, checked in by frichard, 13 years ago

-Version 0.8 de libini
-Formule de Marc
-Nouvelles fonctionnalités (goto nom-de l'objet etc...)

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