source: BAORadio/libindi/libindi/tools/setINDIproperty.c @ 642

Last change on this file since 642 was 642, checked in by frichard, 12 years ago

-Alignement des antennes
-Version 0.0.9 de libindi

File size: 14.6 KB
Line 
1/* connect to an INDI server and set one or more device.property.element.
2 */
3
4#include <stdio.h>
5#include <stdlib.h>
6#include <math.h>
7#include <string.h>
8#include <errno.h>
9#include <signal.h>
10#include <time.h>
11#include <unistd.h>
12#include <sys/types.h>
13#include <sys/socket.h>
14#include <netinet/in.h>
15#include <netdb.h>
16
17#include "indiapi.h"
18#include "lilxml.h"
19
20/* table of INDI definition elements we can set
21 * N.B. do not change defs[] order, they are indexed via -x/-n/-s args
22 */
23typedef struct {
24    char *defType;                      /* defXXXVector name */
25    char *defOne;                       /* defXXX name */
26    char *newType;                      /* newXXXVector name */
27    char *oneType;                      /* oneXXX name */
28} INDIDef;
29static INDIDef defs[] = {
30    {"defTextVector",   "defText",   "newTextVector",   "oneText"},
31    {"defNumberVector", "defNumber", "newNumberVector", "oneNumber"},
32    {"defSwitchVector", "defSwitch", "newSwitchVector", "oneSwitch"},
33};
34#define NDEFS   (sizeof(defs)/sizeof(defs[0]))
35
36#define INDIPORT        7624            /* default port */
37static char host_def[] = "localhost";   /* default host name */
38
39static char *me;                        /* our name for usage message */
40static char *host = host_def;           /* working host name */
41static int port = INDIPORT;             /* working port number */
42static int verbose;                     /* report extra info */
43static int directfd = -1;               /* direct filedes to server, if >= 0 */
44#define TIMEOUT         2               /* default timeout, secs */
45static int timeout = TIMEOUT;           /* working timeout, secs */
46static LilXML *lillp;                   /* XML parser context */
47
48typedef struct {
49    char *e, *v;                        /* element name and value */
50    int ok;                             /* set when found */
51} SetEV;
52
53typedef struct {
54    char *d;                            /* device */
55    char *p;                            /* property */
56    SetEV *ev;                          /* elements */
57    int nev;                            /* n elements */
58    INDIDef *dp;                        /* one of defs if known, else NULL */
59} SetSpec;
60
61static SetSpec *sets;                   /* set of properties to set */
62static int nsets;
63
64
65static void usage (void);
66static int crackSpec (int *acp, char **avp[]);
67static void openINDIServer(FILE **rfpp, FILE **wfpp);
68static void listenINDI (FILE *rfp, FILE *wfp);
69static int finished (void);
70static void onAlarm (int dummy);
71static int readServerChar (FILE *fp);
72static void findSet (XMLEle *root, FILE *fp);
73static void scanEV (SetSpec *specp, char ev[]);
74static void scanEEVV (SetSpec *specp, char *ep, char ev[]);
75static void scanEVEV (SetSpec *specp, char ev[]);
76static void sendNew (FILE *fp, INDIDef *dp, SetSpec *sp);
77static void sendSpecs(FILE *wfp);
78
79int
80main (int ac, char *av[])
81{
82        FILE *rfp, *wfp;
83        int stop = 0;
84        int allspeced;
85
86        /* save our name */
87        me = av[0];
88
89        /* crack args */
90        while (!stop && --ac && **++av == '-') {
91            char *s = *av;
92            while (*++s) {
93                switch (*s) {
94
95                case 'd':
96                    if (ac < 2) {
97                        fprintf (stderr, "-d requires open fileno\n");
98                        usage();
99                    }
100                    directfd = atoi(*++av);
101                    ac--;
102                    break;
103
104                case 'h':
105                    if (directfd >= 0) {
106                        fprintf (stderr, "Can not combine -d and -h\n");
107                        usage();
108                    }
109                    if (ac < 2) {
110                        fprintf (stderr, "-h requires host name\n");
111                        usage();
112                    }
113                    host = *++av;
114                    ac--;
115                    break;
116
117                case 'p':
118                    if (directfd >= 0) {
119                        fprintf (stderr, "Can not combine -d and -p\n");
120                        usage();
121                    }
122                    if (ac < 2) {
123                        fprintf (stderr, "-p requires tcp port number\n");
124                        usage();
125                    }
126                    port = atoi(*++av);
127                    ac--;
128                    break;
129
130                case 't':
131                    if (ac < 2) {
132                        fprintf (stderr, "-t requires timeout\n");
133                        usage();
134                    }
135                    timeout = atoi(*++av);
136                    ac--;
137                    break;
138
139                case 'v':       /* verbose */
140                    verbose++;
141                    break;
142
143                case 'x':       /* FALLTHRU */
144                case 'n':       /* FALLTHRU */
145                case 's':
146                    /* stop if see one of the property types */
147                    stop = 1;
148                    break;
149
150                default:
151                    fprintf (stderr, "Unknown flag: %c\n", *s);
152                    usage();
153                }
154            }
155        }
156
157        /* now ac args starting at av[0] */
158        if (ac < 1)
159            usage();
160
161        /* crack each property, add to sets[]  */
162        allspeced = 1;
163        do {
164            if (!crackSpec (&ac, &av))
165                allspeced = 0;
166        } while (ac > 0);
167
168        /* open connection */
169        if (directfd >= 0) {
170            wfp = fdopen (directfd, "w");
171            rfp = fdopen (directfd, "r");
172            setbuf (rfp, NULL);         /* don't absorb next guy's stuff */
173            if (!rfp || !wfp) {
174                fprintf (stderr, "Direct fd %d: %s\n",directfd,strerror(errno));
175                exit(1);
176            }
177            if (verbose)
178                fprintf (stderr, "Using direct fd %d\n", directfd);
179        } else {
180            openINDIServer(&rfp, &wfp);
181            if (verbose)
182                fprintf (stderr, "Connected to %s on port %d\n", host, port);
183        }
184
185        /* build a parser context for cracking XML responses */
186        lillp = newLilXML();
187
188        /* just send it all speced, else check with server */
189        if (allspeced) {
190            sendSpecs(wfp);
191        } else {
192            /* issue getProperties */
193            if (verbose)
194                fprintf (stderr, "Querying for properties\n");
195            fprintf(wfp, "<getProperties version='%g'/>\n", INDIV);
196            fflush (wfp);
197
198            /* listen for properties, set when see any we recognize */
199            listenINDI(rfp, wfp);
200        }
201
202        return (0);
203}
204
205static void
206usage()
207{
208        fprintf(stderr, "Purpose: set one or more writable INDI properties\n");
209        fprintf(stderr, "%s\n", "$Revision: 1.6 $");
210        fprintf(stderr, "Usage: %s [options] {[type] spec} ...\n", me);
211        fprintf(stderr, "Options:\n");
212        fprintf(stderr, "  -d f  : use file descriptor f already open to server\n");
213        fprintf(stderr, "  -h h  : alternate host, default is %s\n", host_def);
214        fprintf(stderr, "  -p p  : alternate port, default is %d\n", INDIPORT);
215        fprintf(stderr, "  -t t  : max time to wait, default is %d secs\n",TIMEOUT);
216        fprintf(stderr, "  -v    : verbose (more are cumulative)\n");
217        fprintf(stderr, "Each spec optionally preceded by its type is sent without first confirming\n");
218        fprintf(stderr, "its structure. This is much more efficient but there is no error checking.\n");
219        fprintf(stderr, "Types are indicated with the following flags:\n");
220        fprintf(stderr, "  -x    : Text\n");
221        fprintf(stderr, "  -n    : Number\n");
222        fprintf(stderr, "  -s    : Switch\n");
223        fprintf(stderr, "Spec may be either:\n");
224        fprintf(stderr, "    device.property.e1[;e2...]=v1[;v2...]\n");
225        fprintf(stderr, "  or\n");
226        fprintf(stderr, "    device.property.e1=v1[;e2=v2...]\n");
227        fprintf(stderr, "Exit status:\n");
228        fprintf(stderr, "  0: all settings successful\n");
229        fprintf(stderr, "  1: at least one setting was invalid\n");
230        fprintf(stderr, "  2: real trouble, try repeating with -v\n");
231
232        exit (2);
233}
234
235/* crack property set spec, add to sets [], move to next spec.
236 * return 1 if see a type
237 */
238static int
239crackSpec (int *acp, char **avp[])
240{
241        char d[128], p[128], ev[2048];
242        char *spec = *avp[0];
243        INDIDef *dp = NULL;
244
245        /* check if first arg is type indicator */
246        if (spec[0] == '-') {
247            switch (spec[1]) {
248            case 'x':   dp = &defs[0]; break;
249            case 'n':   dp = &defs[1]; break;
250            case 's':   dp = &defs[2]; break;
251            default:
252                fprintf (stderr, "Bad property type: %s\n", spec);
253                usage();
254            }
255            (*acp)--;
256            (*avp)++;
257            spec = *avp[0];
258        }
259
260        /* then scan arg for property spec */
261        if (sscanf (spec, "%[^.].%[^.].%s", d, p, ev) != 3) {
262            fprintf (stderr, "Malformed property spec: %s\n", spec);
263            usage();
264        }
265
266        /* add to list */
267        if (!sets)
268            sets = (SetSpec *) malloc (1);              /* seed realloc */
269        sets = (SetSpec *) realloc (sets, (nsets+1)*sizeof(SetSpec));
270        sets[nsets].d = strcpy (malloc(strlen(d)+1), d);
271        sets[nsets].p = strcpy (malloc(strlen(p)+1), p);
272        sets[nsets].dp = dp;
273        sets[nsets].ev = (SetEV *) malloc (1);          /* seed realloc */
274        sets[nsets].nev = 0;
275        scanEV (&sets[nsets++], ev);
276
277        /* update caller's pointers */
278        (*acp)--;
279        (*avp)++;
280
281        /* return 1 if saw a spec */
282        return (dp ? 1 : 0);
283}
284
285/* open a read and write connection to host and port or die.
286 * exit if trouble.
287 */
288static void
289openINDIServer (FILE **rfpp, FILE **wfpp)
290{
291        struct sockaddr_in serv_addr;
292        struct hostent *hp;
293        int sockfd;
294
295        /* lookup host address */
296        hp = gethostbyname (host);
297        if (!hp) {
298            perror ("gethostbyname");
299            exit (2);
300        }
301
302        /* create a socket to the INDI server */
303        (void) memset ((char *)&serv_addr, 0, sizeof(serv_addr));
304        serv_addr.sin_family = AF_INET;
305        serv_addr.sin_addr.s_addr =
306                            ((struct in_addr *)(hp->h_addr_list[0]))->s_addr;
307        serv_addr.sin_port = htons(port);
308        if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
309            perror ("socket");
310            exit(2);
311        }
312
313        /* connect */
314        if (connect (sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr))<0){
315            perror ("connect");
316            exit(2);
317        }
318
319        /* prepare for line-oriented i/o to client */
320        *rfpp = fdopen (sockfd, "r");
321        *wfpp = fdopen (sockfd, "w");
322}
323
324/* listen for property reports, send new sets if match */
325static void
326listenINDI (FILE *rfp, FILE *wfp)
327{
328        char msg[1024];
329
330        /* arrange to call onAlarm() if not seeing any more defXXX */
331        signal (SIGALRM, onAlarm);
332        alarm (timeout);
333
334        /* read from server, exit if find all properties */
335        while (1) {
336            XMLEle *root = readXMLEle (lillp, readServerChar(rfp), msg);
337            if (root) {
338                /* found a complete XML element */
339                if (verbose > 1)
340                    prXMLEle (stderr, root, 0);
341                findSet (root, wfp);
342                if (finished() == 0) {
343                    shutdown (fileno(wfp), SHUT_WR);    /* insure flush */
344                    exit (0);           /* found all we want */
345                }
346                delXMLEle (root);       /* not yet, delete and continue */
347            } else if (msg[0]) {
348                fprintf (stderr, "Bad XML from %s/%d: %s\n", host, port, msg);
349                exit(2);
350            }
351        }
352}
353
354/* return 0 if we are sure we set everything we wanted to, else -1 */
355static int
356finished ()
357{
358        int i, j;
359
360        for (i = 0; i < nsets; i++)
361            for (j = 0; j < sets[i].nev; j++)
362                if (!sets[i].ev[j].ok)
363                    return (-1);
364        return (0);
365}
366
367/* called after timeout seconds because we did not find something we trying
368 * to set.
369 */
370static void
371onAlarm (int dummy)
372{
373        int i, j;
374
375        for (i = 0; i < nsets; i++)
376            for (j = 0; j < sets[i].nev; j++)
377                if (!sets[i].ev[j].ok)
378                    fprintf (stderr, "No %s.%s.%s from %s:%d\n", sets[i].d,
379                                    sets[i].p, sets[i].ev[j].e, host, port);
380
381        exit (1);
382}
383
384static int
385readServerChar (FILE *fp)
386{
387        int c = fgetc (fp);
388
389        if (c == EOF) {
390            if (ferror(fp))
391                perror ("read");
392            else
393                fprintf (stderr,"INDI server %s:%d disconnected\n", host, port);
394            exit (2);
395        }
396
397        if (verbose > 2)
398            fprintf (stderr, "Read %c\n", c);
399
400        return (c);
401}
402
403/* issue a set command if it matches the given property */
404static void
405findSet (XMLEle *root, FILE *fp)
406{
407        char *rtype, *rdev, *rprop;
408        XMLEle *ep;
409        int t, s, i;
410
411        /* check type */
412        rtype = tagXMLEle (root);
413        for (t = 0; t < NDEFS; t++)
414            if (strcmp (rtype, defs[t].defType) == 0)
415                break;
416        if (t == NDEFS)
417            return;
418        alarm (timeout);        /* reset timeout */
419
420        /* check each set for matching device and property name, send if ok */
421        rdev  = (char *) findXMLAttValu (root, "device");
422        rprop = (char *) findXMLAttValu (root, "name");
423        if (verbose > 1)
424            fprintf (stderr, "Read definition for %s.%s\n", rdev, rprop);
425        for (s = 0; s < nsets; s++) {
426            if (!strcmp (rdev, sets[s].d) && !strcmp (rprop, sets[s].p)) {
427                /* found device and name,  check perm */
428                if (!strchr (findXMLAttValu (root, "perm"), 'w')) {
429                    if (verbose)
430                        fprintf (stderr, "%s.%s is read-only\n", rdev, rprop);
431                    exit (1);
432                }
433                /* check matching elements */
434                for (i = 0; i < sets[s].nev; i++) {
435                    for (ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) {
436                        if (!strcmp(findXMLAttValu (ep,"name"),sets[s].ev[i].e)
437                                    && !strcmp(tagXMLEle(ep), defs[t].defOne)) {
438                            sets[s].ev[i].ok = 1;
439                            break;
440                        }
441                    }
442                    if (!ep)
443                        return;         /* not in this msg, maybe later */
444                }
445                /* all element names found, send new values */
446                sendNew (fp, &defs[t], &sets[s]);
447            }
448        }
449}
450
451/* send the given set specification of the given INDI type to channel on fp */
452static void
453sendNew (FILE *fp, INDIDef *dp, SetSpec *sp)
454{
455        int i;
456
457        fprintf (fp, "<%s device='%s' name='%s'>\n", dp->newType, sp->d, sp->p);
458        for (i = 0; i < sp->nev; i++) {
459            if (verbose)
460                fprintf (stderr, "  %s.%s.%s <- %s\n", sp->d, sp->p,
461                                                    sp->ev[i].e, sp->ev[i].v);
462            fprintf (fp, "  <%s name='%s'>%s</%s>\n", dp->oneType, sp->ev[i].e,
463                                                    sp->ev[i].v, dp->oneType);
464        }
465        fprintf (fp, "</%s>\n", dp->newType);
466        fflush (fp);
467        if (feof(fp) || ferror(fp)) {
468            fprintf (stderr, "Send error\n");
469            exit(2);
470        }
471}
472
473/* scan ev for element definitions in either of two forms and add to sp:
474 *    e1[;e2...]=v1[;v2...]
475 *  or
476 *    e1=v1[;e2=v2...]
477 * exit if nothing sensible found.
478 */
479static void
480scanEV (SetSpec *specp, char ev[])
481{
482        char *ep, *sp;          /* pointers to = and ; */
483
484        if (verbose > 1)
485            fprintf (stderr, "Scanning assignments %s\n", ev);
486
487        ep = strchr (ev, '=');
488        sp = strchr (ev, ';');
489
490        if (!ep) {
491            fprintf (stderr, "Malformed assignment: %s\n", ev);
492            usage();
493        }
494
495        if (sp < ep)
496            scanEEVV (specp, ep, ev);   /* including just one E=V */
497        else
498            scanEVEV (specp, ev);
499}
500
501/* add specs of the form e1[;e2...]=v1[;v2...] to sp.
502 * v is pointer to equal sign.
503 * exit if trouble.
504 * N.B. e[] and v[] are modified in place.
505 */
506static void
507scanEEVV (SetSpec *sp, char *v, char *e)
508{
509        static char sep[] = ";";
510        char *ec, *vc;
511
512        *v++ = '\0';
513
514        while (1) {
515            char *e0 = strtok_r (e, sep, &ec);
516            char *v0 = strtok_r (v, sep, &vc);
517
518            if (!e0 && !v0)
519                break;
520            if (!e0) {
521                fprintf (stderr, "More values than elements for %s.%s\n", sp->d, sp->p);
522                exit(2);
523            }
524            if (!v0) {
525                fprintf (stderr, "More elements than values for %s.%s\n", sp->d, sp->p);
526                exit(2);
527            }
528
529            sp->ev = (SetEV *) realloc (sp->ev, (sp->nev+1)*sizeof(SetEV));
530            sp->ev[sp->nev].e = strcpy (malloc(strlen(e0)+1), e0);
531            sp->ev[sp->nev].v = strcpy (malloc(strlen(v0)+1), v0);
532            if (verbose > 1)
533                fprintf (stderr, "Found assignment %s=%s\n", sp->ev[sp->nev].e, sp->ev[sp->nev].v);
534            sp->nev++;
535
536            e = NULL;
537            v = NULL;
538        }
539}
540
541/* add specs of the form e1=v1[;e2=v2...] to sp.
542 * exit if trouble.
543 * N.B. ev[] is modified in place.
544 */
545static void
546scanEVEV (SetSpec *sp, char ev[])
547{
548        char *s, *e;
549        int last = 0;
550
551        do {
552            s = strchr (ev, ';');
553            if (s)
554                *s++ = '\0';
555            else {
556                s = ev + strlen (ev);
557                last = 1;
558            }
559            e = strchr (ev, '=');
560            if (e)
561                *e++ = '\0';
562            else {
563                fprintf (stderr, "Malformed assignment: %s\n", ev);
564                usage();
565            }
566
567            sp->ev = (SetEV *) realloc (sp->ev, (sp->nev+1)*sizeof(SetEV));
568            sp->ev[sp->nev].e = strcpy (malloc(strlen(ev)+1), ev);
569            sp->ev[sp->nev].v = strcpy (malloc(strlen(e)+1), e);
570            if (verbose > 1)
571                fprintf (stderr, "Found assignment %s=%s\n", sp->ev[sp->nev].e, sp->ev[sp->nev].v);
572            sp->nev++;
573
574            ev = s;
575
576        } while (!last);
577}
578
579/* send each SetSpec, all of which have a known type, to wfp
580 */
581static void
582sendSpecs(FILE *wfp)
583{
584        int i;
585       
586        for (i = 0; i < nsets; i++)
587            sendNew (wfp, sets[i].dp, &sets[i]);
588}
589
590/* For RCS Only -- Do Not Edit */
591static char *rcsid[2] = {(char *)rcsid, "@(#) $RCSfile: setINDI.c,v $ $Date: 2010/11/07 07:13:59 $ $Revision: 1.6 $ $Name:  $"};
Note: See TracBrowser for help on using the repository browser.