source: BAORadio/libindi/v1/tools/setINDIproperty.c @ 612

Last change on this file since 612 was 490, checked in by campagne, 14 years ago

import libindi (JEC)

File size: 12.9 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 *sp, char e[], char v[]);
74static void sendNew (FILE *fp, INDIDef *dp, SetSpec *sp);
75static void sendSpecs(FILE *wfp);
76
77int
78main (int ac, char *av[])
79{
80        FILE *rfp, *wfp;
81        int stop = 0;
82        int allspeced;
83
84        /* save our name */
85        me = av[0];
86
87        /* crack args */
88        while (!stop && --ac && **++av == '-') {
89            char *s = *av;
90            while (*++s) {
91                switch (*s) {
92
93                case 'd':
94                    if (ac < 2) {
95                        fprintf (stderr, "-d requires open fileno\n");
96                        usage();
97                    }
98                    directfd = atoi(*++av);
99                    ac--;
100                    break;
101
102                case 'h':
103                    if (directfd >= 0) {
104                        fprintf (stderr, "Can not combine -d and -h\n");
105                        usage();
106                    }
107                    if (ac < 2) {
108                        fprintf (stderr, "-h requires host name\n");
109                        usage();
110                    }
111                    host = *++av;
112                    ac--;
113                    break;
114
115                case 'p':
116                    if (directfd >= 0) {
117                        fprintf (stderr, "Can not combine -d and -p\n");
118                        usage();
119                    }
120                    if (ac < 2) {
121                        fprintf (stderr, "-p requires tcp port number\n");
122                        usage();
123                    }
124                    port = atoi(*++av);
125                    ac--;
126                    break;
127
128                case 't':
129                    if (ac < 2) {
130                        fprintf (stderr, "-t requires timeout\n");
131                        usage();
132                    }
133                    timeout = atoi(*++av);
134                    ac--;
135                    break;
136
137                case 'v':       /* verbose */
138                    verbose++;
139                    break;
140
141                case 'x':       /* FALLTHRU */
142                case 'n':       /* FALLTHRU */
143                case 's':
144                    /* stop if see one of the property types */
145                    stop = 1;
146                    break;
147
148                default:
149                    fprintf (stderr, "Unknown flag: %c\n", *s);
150                    usage();
151                }
152            }
153        }
154
155        /* now ac args starting at av[0] */
156        if (ac < 1)
157            usage();
158
159        /* crack each property, add to sets[]  */
160        allspeced = 1;
161        do {
162            if (!crackSpec (&ac, &av))
163                allspeced = 0;
164        } while (ac > 0);
165
166        /* open connection */
167        if (directfd >= 0) {
168            wfp = fdopen (directfd, "w");
169            rfp = fdopen (directfd, "r");
170            setbuf (rfp, NULL);         /* don't absorb next guy's stuff */
171            if (!rfp || !wfp) {
172                fprintf (stderr, "Direct fd %d: %s\n",directfd,strerror(errno));
173                exit(1);
174            }
175            if (verbose)
176                fprintf (stderr, "Using direct fd %d\n", directfd);
177        } else {
178            openINDIServer(&rfp, &wfp);
179            if (verbose)
180                fprintf (stderr, "Connected to %s on port %d\n", host, port);
181        }
182
183        /* build a parser context for cracking XML responses */
184        lillp = newLilXML();
185
186        /* just send it all speced, else check with server */
187        if (allspeced) {
188            sendSpecs(wfp);
189        } else {
190            /* issue getProperties */
191            if (verbose)
192                fprintf (stderr, "Querying for properties\n");
193            fprintf(wfp, "<getProperties version='%g'/>\n", INDIV);
194            fflush (wfp);
195
196            /* listen for properties, set when see any we recognize */
197            listenINDI(rfp, wfp);
198        }
199
200        return (0);
201}
202
203static void
204usage()
205{
206        fprintf(stderr, "Purpose: set one or more writable INDI properties\n");
207        fprintf(stderr, "%s\n", "$Revision: 1.4 $");
208        fprintf(stderr, "Usage: %s [options] {[type] device.property.e1[;e2...]=v1[;v2...]} ...\n",
209                                                                            me);
210        fprintf(stderr, "Options:\n");
211        fprintf(stderr, "  -d f  : use file descriptor f already open to server\n");
212        fprintf(stderr, "  -h h  : alternate host, default is %s\n", host_def);
213        fprintf(stderr, "  -p p  : alternate port, default is %d\n", INDIPORT);
214        fprintf(stderr, "  -t t  : max time to wait, default is %d secs\n",TIMEOUT);
215        fprintf(stderr, "  -v    : verbose (cumulative)\n");
216        fprintf(stderr, "Each property optionally preceded by its type is sent without first confirming\n");
217        fprintf(stderr, "its structure. This is much more efficient but there is no error checking.\n");
218        fprintf(stderr, "Types are indicated with the following flags:\n");
219        fprintf(stderr, "  -x    : Text\n");
220        fprintf(stderr, "  -n    : Number\n");
221        fprintf(stderr, "  -s    : Switch\n");
222        fprintf(stderr, "Exit status:\n");
223        fprintf(stderr, "  0: all settings successful\n");
224        fprintf(stderr, "  1: at least one setting was invalid\n");
225        fprintf(stderr, "  2: real trouble, try repeating with -v\n");
226
227        exit (2);
228}
229
230/* crack property set spec, add to sets [], move to next spec.
231 * return 1 if see a type
232 */
233static int
234crackSpec (int *acp, char **avp[])
235{
236        char d[1024], p[1024], e[2048], v[2048];
237        char *spec = *avp[0];
238        INDIDef *dp = NULL;
239
240        /* check if first arg is type indicator */
241        if (spec[0] == '-') {
242            switch (spec[1]) {
243            case 'x':   dp = &defs[0]; break;
244            case 'n':   dp = &defs[1]; break;
245            case 's':   dp = &defs[2]; break;
246            default:
247                fprintf (stderr, "Bad property type: %s\n", spec);
248                usage();
249            }
250            (*acp)--;
251            (*avp)++;
252            spec = *avp[0];
253        }
254
255        /* then scan arg for property spec */
256        if (sscanf (spec, "%[^.].%[^.].%[^.=]=%s", d, p, e, v) != 4) {
257            fprintf (stderr, "Bad property format: %s\n", spec);
258            usage();
259        }
260
261        /* add to list */
262        if (!sets)
263            sets = (SetSpec *) malloc (1);              /* seed realloc */
264        sets = (SetSpec *) realloc (sets, (nsets+1)*sizeof(SetSpec));
265        sets[nsets].d = strcpy (malloc(strlen(d)+1), d);
266        sets[nsets].p = strcpy (malloc(strlen(p)+1), p);
267        sets[nsets].dp = dp;
268        sets[nsets].ev = (SetEV *) malloc (1);          /* seed realloc */
269        sets[nsets].nev = 0;
270        scanEV (&sets[nsets++], e, v);
271
272        /* update caller's pointers */
273        (*acp)--;
274        (*avp)++;
275
276        /* return 1 if saw a spec */
277        return (dp ? 1 : 0);
278}
279
280/* open a read and write connection to host and port or die.
281 * exit if trouble.
282 */
283static void
284openINDIServer (FILE **rfpp, FILE **wfpp)
285{
286        struct sockaddr_in serv_addr;
287        struct hostent *hp;
288        int sockfd;
289
290        /* lookup host address */
291        hp = gethostbyname (host);
292        if (!hp) {
293            perror ("gethostbyname");
294            exit (2);
295        }
296
297        /* create a socket to the INDI server */
298        (void) memset ((char *)&serv_addr, 0, sizeof(serv_addr));
299        serv_addr.sin_family = AF_INET;
300        serv_addr.sin_addr.s_addr =
301                            ((struct in_addr *)(hp->h_addr_list[0]))->s_addr;
302        serv_addr.sin_port = htons(port);
303        if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
304            perror ("socket");
305            exit(2);
306        }
307
308        /* connect */
309        if (connect (sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr))<0){
310            perror ("connect");
311            exit(2);
312        }
313
314        /* prepare for line-oriented i/o to client */
315        *rfpp = fdopen (sockfd, "r");
316        *wfpp = fdopen (sockfd, "w");
317}
318
319/* listen for property reports, send new sets if match */
320static void
321listenINDI (FILE *rfp, FILE *wfp)
322{
323        char msg[1024];
324
325        /* arrange to call onAlarm() if not seeing any more defXXX */
326        signal (SIGALRM, onAlarm);
327        alarm (timeout);
328
329        /* read from server, exit if find all properties */
330        while (1) {
331            XMLEle *root = readXMLEle (lillp, readServerChar(rfp), msg);
332            if (root) {
333                /* found a complete XML element */
334                if (verbose > 1)
335                    prXMLEle (stderr, root, 0);
336                findSet (root, wfp);
337                if (finished() == 0) {
338                    shutdown (fileno(wfp), SHUT_WR);    /* insure flush */
339                    exit (0);           /* found all we want */
340                }
341                delXMLEle (root);       /* not yet, delete and continue */
342            } else if (msg[0]) {
343                fprintf (stderr, "Bad XML from %s/%d: %s\n", host, port, msg);
344                exit(2);
345            }
346        }
347}
348
349/* return 0 if we are sure we set everything we wanted to, else -1 */
350static int
351finished ()
352{
353        int i, j;
354
355        for (i = 0; i < nsets; i++)
356            for (j = 0; j < sets[i].nev; j++)
357                if (!sets[i].ev[j].ok)
358                    return (-1);
359        return (0);
360}
361
362/* called after timeout seconds because we did not find something we trying
363 * to set.
364 */
365static void
366onAlarm (int dummy)
367{
368        int i, j;
369
370        for (i = 0; i < nsets; i++)
371            for (j = 0; j < sets[i].nev; j++)
372                if (!sets[i].ev[j].ok)
373                    fprintf (stderr, "No %s.%s.%s from %s:%d\n", sets[i].d,
374                                    sets[i].p, sets[i].ev[j].e, host, port);
375
376        exit (1);
377}
378
379static int
380readServerChar (FILE *fp)
381{
382        int c = fgetc (fp);
383
384        if (c == EOF) {
385            if (ferror(fp))
386                perror ("read");
387            else
388                fprintf (stderr,"INDI server %s:%d disconnected\n", host, port);
389            exit (2);
390        }
391
392        if (verbose > 2)
393            fprintf (stderr, "Read %c\n", c);
394
395        return (c);
396}
397
398/* issue a set command if it matches the given property */
399static void
400findSet (XMLEle *root, FILE *fp)
401{
402        char *rtype, *rdev, *rprop;
403        XMLEle *ep;
404        int t, s, i;
405
406        /* check type */
407        rtype = tagXMLEle (root);
408        for (t = 0; t < NDEFS; t++)
409            if (strcmp (rtype, defs[t].defType) == 0)
410                break;
411        if (t == NDEFS)
412            return;
413        alarm (timeout);        /* reset timeout */
414
415        /* check each set for matching device and property name, send if ok */
416        rdev  = findXMLAttValu (root, "device");
417        rprop = findXMLAttValu (root, "name");
418        if (verbose > 1)
419            fprintf (stderr, "Read definition for %s.%s\n", rdev, rprop);
420        for (s = 0; s < nsets; s++) {
421            if (!strcmp (rdev, sets[s].d) && !strcmp (rprop, sets[s].p)) {
422                /* found device and name,  check perm */
423                if (!strchr (findXMLAttValu (root, "perm"), 'w')) {
424                    if (verbose)
425                        fprintf (stderr, "%s.%s is read-only\n", rdev, rprop);
426                    exit (1);
427                }
428                /* check matching elements */
429                for (i = 0; i < sets[s].nev; i++) {
430                    for (ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) {
431                        if (!strcmp(findXMLAttValu (ep,"name"),sets[s].ev[i].e)
432                                    && !strcmp(tagXMLEle(ep), defs[t].defOne)) {
433                            sets[s].ev[i].ok = 1;
434                            break;
435                        }
436                    }
437                    if (!ep)
438                        return;         /* not in this msg, maybe later */
439                }
440                /* all element names found, send new values */
441                sendNew (fp, &defs[t], &sets[s]);
442            }
443        }
444}
445
446/* send the given set specification of the given INDI type to channel on fp */
447static void
448sendNew (FILE *fp, INDIDef *dp, SetSpec *sp)
449{
450        int i;
451
452        fprintf (fp, "<%s device='%s' name='%s'>\n", dp->newType, sp->d, sp->p);
453        for (i = 0; i < sp->nev; i++) {
454            if (verbose)
455                fprintf (stderr, "  %s.%s.%s <- %s\n", sp->d, sp->p,
456                                                    sp->ev[i].e, sp->ev[i].v);
457            fprintf (fp, "  <%s name='%s'>%s</%s>\n", dp->oneType, sp->ev[i].e,
458                                                    sp->ev[i].v, dp->oneType);
459        }
460        fprintf (fp, "</%s>\n", dp->newType);
461        fflush (fp);
462        if (feof(fp) || ferror(fp)) {
463            fprintf (stderr, "Send error\n");
464            exit(2);
465        }
466}
467
468/* scan e1[;e2...] v1[;v2,...] from e[] and v[] and add to spec sp.
469 * exit if trouble.
470 */
471static void
472scanEV (SetSpec *sp, char e[], char v[])
473{
474        static char sep[] = ";";
475        char *ep, *vp;
476
477        if (verbose > 1)
478            fprintf (stderr, "Scanning %s = %s\n", e, v);
479
480        while (1) {
481            char *e0 = strtok_r (e, sep, &ep);
482            char *v0 = strtok_r (v, sep, &vp);
483
484            if (!e0 && !v0)
485                break;
486            if (!e0) {
487                fprintf (stderr, "More values than elements for %s.%s\n",
488                                                                sp->d, sp->p);
489                exit(2);
490            }
491            if (!v0) {
492                fprintf (stderr, "More elements than values for %s.%s\n",
493                                                                sp->d, sp->p);
494                exit(2);
495            }
496
497            sp->ev = (SetEV *) realloc (sp->ev, (sp->nev+1)*sizeof(SetEV));
498            sp->ev[sp->nev].e = strcpy (malloc(strlen(e0)+1), e0);
499            sp->ev[sp->nev].v = strcpy (malloc(strlen(v0)+1), v0);
500            sp->nev++;
501
502            e = NULL;
503            v = NULL;
504        }
505}
506
507/* send each SetSpec, all of which have a known type, to wfp
508 */
509static void
510sendSpecs(FILE *wfp)
511{
512        int i;
513       
514        for (i = 0; i < nsets; i++)
515            sendNew (wfp, sets[i].dp, &sets[i]);
516}
517
518/* For RCS Only -- Do Not Edit */
519static char *rcsid[2] = {(char *)rcsid, "@(#) $RCSfile: setINDI.c,v $ $Date: 2007/10/11 20:12:11 $ $Revision: 1.4 $ $Name:  $"};
Note: See TracBrowser for help on using the repository browser.