Instrument Neutral Distributed Interface INDI 2.2.5
getINDIdevice.c
Go to the documentation of this file.
1/* connect to an INDI server and list all devices
2 * exit status: 0 at least one device found, 1 no devices found, 2 real trouble.
3 */
4
5#include "indiapi.h"
6#include "lilxml.h"
7
8#include <errno.h>
9#include <netdb.h>
10#include <signal.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <time.h>
15#include <unistd.h>
16#include <netinet/in.h>
17#include <sys/types.h>
18#include <sys/socket.h>
19
20/* table of INDI definition elements we look for */
21typedef struct
22{
23 char *vec; /* vector name */
24} INDIDef;
25static INDIDef defs[] = {
26 { "defTextVector" },
27 { "defNumberVector" },
28 { "defSwitchVector" },
29 { "defLightVector" },
30 { "defBLOBVector" },
31};
32static int ndefs = sizeof(defs) / sizeof(defs[0]);
33
34/* simple device list to track unique devices */
35typedef struct
36{
37 char **devices;
38 int count;
40static DeviceList devlist = { NULL, 0 };
41
42static void usage(void);
43static void addDevice(const char *dev);
44static int hasDevice(const char *dev);
45static int matchPattern(const char *str, const char *pattern);
46static void openINDIServer(void);
47static void getprops(void);
48static void listenINDI(void);
49static void onAlarm(int dummy);
50static int readServerChar(void);
51static void findDevices(XMLEle *root);
52
53static char *me; /* our name for usage() message */
54static char host_def[] = "localhost"; /* default host name */
55static char *host = host_def; /* working host name */
56#define INDIPORT 7624 /* default port */
57static int port = INDIPORT; /* working port number */
58#define TIMEOUT 2 /* default timeout, secs */
59static int timeout = TIMEOUT; /* working timeout, secs */
60static int verbose; /* report extra info */
61static LilXML *lillp; /* XML parser context */
62#define WILDCARD '*' /* wildcard character */
63static int directfd = -1; /* direct filedes to server, if >= 0 */
64static FILE *svrwfp; /* FILE * to talk to server */
65static FILE *svrrfp; /* FILE * to read from server */
66static char *devpattern = NULL; /* device pattern to match */
67
68int main(int ac, char *av[])
69{
70 /* save our name */
71 me = av[0];
72
73 /* crack args */
74 while (--ac && **++av == '-')
75 {
76 char *s = *av;
77 while (*++s)
78 {
79 switch (*s)
80 {
81 case 'd':
82 if (ac < 2)
83 {
84 fprintf(stderr, "-d requires open fileno\n");
85 usage();
86 }
87 directfd = atoi(*++av);
88 ac--;
89 break;
90 case 'h':
91 if (directfd >= 0)
92 {
93 fprintf(stderr, "Can not combine -d and -h\n");
94 usage();
95 }
96 if (ac < 2)
97 {
98 fprintf(stderr, "-h requires host name\n");
99 usage();
100 }
101 host = *++av;
102 ac--;
103 break;
104 case 'p':
105 if (directfd >= 0)
106 {
107 fprintf(stderr, "Can not combine -d and -p\n");
108 usage();
109 }
110 if (ac < 2)
111 {
112 fprintf(stderr, "-p requires tcp port number\n");
113 usage();
114 }
115 port = atoi(*++av);
116 ac--;
117 break;
118 case 't':
119 if (ac < 2)
120 {
121 fprintf(stderr, "-t requires timeout\n");
122 usage();
123 }
124 timeout = atoi(*++av);
125 ac--;
126 break;
127 case 'v': /* verbose */
128 verbose++;
129 break;
130 default:
131 fprintf(stderr, "Unknown flag: %c\n", *s);
132 usage();
133 }
134 }
135 }
136
137 /* now ac args starting with av[0] */
138 if (ac > 0)
139 devpattern = *av; /* optional device pattern */
140
141 /* open connection */
142 if (directfd >= 0)
143 {
144 svrwfp = fdopen(directfd, "w");
145 svrrfp = fdopen(directfd, "r");
146 if (!svrwfp || !svrrfp)
147 {
148 fprintf(stderr, "Direct fd %d: %s\n", directfd, strerror(errno));
149 exit(2);
150 }
151 setbuf(svrrfp, NULL); /* don't absorb next guy's stuff */
152 if (verbose)
153 fprintf(stderr, "Using direct fd %d\n", directfd);
154 }
155 else
156 {
157 openINDIServer();
158 if (verbose)
159 fprintf(stderr, "Connected to %s on port %d\n", host, port);
160 }
161
162 /* build a parser context for cracking XML responses */
163 lillp = newLilXML();
164
165 /* issue getProperties */
166 getprops();
167
168 /* listen for responses, looking for devices or timeout */
169 listenINDI();
170
171 /* print all found devices */
172 for (int i = 0; i < devlist.count; i++)
173 {
174 printf("%s\n", devlist.devices[i]);
175 }
176
177 /* clean up */
178 for (int i = 0; i < devlist.count; i++)
179 free(devlist.devices[i]);
180 free(devlist.devices);
181 delLilXML(lillp);
182
183 return (devlist.count > 0 ? 0 : 1);
184}
185
186static void usage()
187{
188 fprintf(stderr, "Purpose: list devices from an INDI server\n");
189 fprintf(stderr, "%s\n", GIT_TAG_STRING);
190 fprintf(stderr, "Usage: %s [options] [device_pattern]\n", me);
191 fprintf(stderr, " device_pattern may contain \"*\" to match all (beware shell metacharacters).\n");
192 fprintf(stderr, " Lists all devices if no pattern specified.\n");
193 fprintf(stderr, "Options:\n");
194 fprintf(stderr, " -d f : use file descriptor f already open to server\n");
195 fprintf(stderr, " -h h : alternate host, default is %s\n", host_def);
196 fprintf(stderr, " -p p : alternate port, default is %d\n", INDIPORT);
197 fprintf(stderr, " -t t : max time to wait, default is %d secs\n", TIMEOUT);
198 fprintf(stderr, " -v : verbose (cumulative)\n");
199 fprintf(stderr, "Exit status:\n");
200 fprintf(stderr, " 0: found at least one device\n");
201 fprintf(stderr, " 1: no devices found\n");
202 fprintf(stderr, " 2: real trouble, try repeating with -v\n");
203
204 exit(2);
205}
206
207/* add a device to the list if not already present and matches pattern */
208static void addDevice(const char *dev)
209{
210 /* check if matches pattern */
211 if (devpattern && !matchPattern(dev, devpattern))
212 return;
213
214 /* check if already in list */
215 if (hasDevice(dev))
216 return;
217
218 /* add to list */
219 devlist.devices = (char **)realloc(devlist.devices, (devlist.count + 1) * sizeof(char *));
220 devlist.devices[devlist.count] = strdup(dev);
221 devlist.count++;
222
223 if (verbose)
224 fprintf(stderr, "Found device: %s\n", dev);
225}
226
227/* check if device is already in list */
228static int hasDevice(const char *dev)
229{
230 for (int i = 0; i < devlist.count; i++)
231 {
232 if (strcmp(devlist.devices[i], dev) == 0)
233 return 1;
234 }
235 return 0;
236}
237
238/* simple wildcard matching: supports * only */
239static int matchPattern(const char *str, const char *pattern)
240{
241 /* if pattern is just *, match everything */
242 if (strcmp(pattern, "*") == 0)
243 return 1;
244
245 /* check if pattern has wildcard */
246 const char *star = strchr(pattern, WILDCARD);
247 if (!star)
248 {
249 /* no wildcard, exact match */
250 return strcmp(str, pattern) == 0;
251 }
252
253 /* wildcard present */
254 int prefix_len = star - pattern;
255
256 /* check prefix */
257 if (strncmp(str, pattern, prefix_len) != 0)
258 return 0;
259
260 /* if wildcard is at the end, we're done */
261 if (star[1] == '\0')
262 return 1;
263
264 /* check suffix */
265 const char *suffix = star + 1;
266 int suffix_len = strlen(suffix);
267 int str_len = strlen(str);
268
269 if (str_len < prefix_len + suffix_len)
270 return 0;
271
272 return strcmp(str + str_len - suffix_len, suffix) == 0;
273}
274
275/* open a connection to the given host and port.
276 * set svrwfp and svrrfp or die.
277 */
278static void openINDIServer(void)
279{
280 struct sockaddr_in serv_addr;
281 struct hostent *hp;
282 int sockfd;
283
284 /* lookup host address */
285 hp = gethostbyname(host);
286 if (!hp)
287 {
288 herror("gethostbyname");
289 exit(2);
290 }
291
292 /* create a socket to the INDI server */
293 (void)memset((char *)&serv_addr, 0, sizeof(serv_addr));
294 serv_addr.sin_family = AF_INET;
295 serv_addr.sin_addr.s_addr = ((struct in_addr *)(hp->h_addr_list[0]))->s_addr;
296 serv_addr.sin_port = htons(port);
297 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
298 {
299 perror("socket");
300 exit(2);
301 }
302
303 /* connect */
304 if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
305 {
306 perror("connect");
307 exit(2);
308 }
309
310 /* prepare for line-oriented i/o with client */
311 svrwfp = fdopen(sockfd, "w");
312 svrrfp = fdopen(sockfd, "r");
313}
314
315/* issue getProperties to svrwfp */
316static void getprops()
317{
318 fprintf(svrwfp, "<getProperties version='%g'/>\n", INDIV);
319 fflush(svrwfp);
320
321 if (verbose)
322 fprintf(stderr, "Queried properties from server\n");
323}
324
325/* listen for INDI traffic on svrrfp.
326 * collect device names and return on timeout.
327 */
328static void listenINDI()
329{
330 char msg[1024];
331
332 /* arrange to call onAlarm() on timeout */
333 signal(SIGALRM, onAlarm);
334 alarm(timeout);
335
336 /* read from server until timeout */
337 while (1)
338 {
339 XMLEle *root = readXMLEle(lillp, readServerChar(), msg);
340 if (root)
341 {
342 /* found a complete XML element */
343 if (verbose > 1)
344 prXMLEle(stderr, root, 0);
345 findDevices(root);
346 delXMLEle(root);
347 }
348 else if (msg[0])
349 {
350 fprintf(stderr, "Bad XML from %s/%d: %s\n", host, port, msg);
351 exit(2);
352 }
353 }
354}
355
356/* called after timeout seconds */
357static void onAlarm(int dummy)
358{
359 (void)dummy;
360
361 /* print all found devices */
362 for (int i = 0; i < devlist.count; i++)
363 {
364 printf("%s\n", devlist.devices[i]);
365 }
366
367 /* clean up */
368 for (int i = 0; i < devlist.count; i++)
369 free(devlist.devices[i]);
370 free(devlist.devices);
371 delLilXML(lillp);
372
373 exit(devlist.count > 0 ? 0 : 1);
374}
375
376/* read one char from svrrfp */
377static int readServerChar()
378{
379 int c = fgetc(svrrfp);
380
381 if (c == EOF)
382 {
383 if (ferror(svrrfp))
384 perror("read");
385 else
386 fprintf(stderr, "INDI server %s/%d disconnected\n", host, port);
387 exit(2);
388 }
389
390 if (verbose > 2)
391 fprintf(stderr, "Read %c\n", c);
392
393 return (c);
394}
395
396/* extract device name from root if it's a defXXXVector */
397static void findDevices(XMLEle *root)
398{
399 const char *tag = tagXMLEle(root);
400
401 /* check if this is a definition vector */
402 for (int i = 0; i < ndefs; i++)
403 {
404 if (strcmp(tag, defs[i].vec) == 0)
405 {
406 /* found a defXXXVector, get device name */
407 const char *dev = findXMLAttValu(root, "device");
408 if (dev && dev[0])
409 {
410 addDevice(dev);
411 /* reset alarm since we got new data */
412 alarm(timeout);
413 }
414 break;
415 }
416 }
417}
int main()
int errno
#define TIMEOUT
#define WILDCARD
#define INDIPORT
Constants and Data structure definitions for the interface to the reference INDI C API implementation...
#define INDIV
Definition indiapi.h:134
int verbose
Definition indidriver.c:49
char * me
Definition indidriver.c:50
void usage(void)
const char * findXMLAttValu(XMLEle *ep, const char *name)
Find an XML element's attribute value.
Definition lilxml.cpp:644
LilXML * newLilXML()
Create a new lilxml parser.
Definition lilxml.cpp:150
char * tagXMLEle(XMLEle *ep)
Return the tag of an XML element.
Definition lilxml.cpp:600
void prXMLEle(FILE *fp, XMLEle *ep, int level)
Print an XML element.
Definition lilxml.cpp:844
XMLEle * readXMLEle(LilXML *lp, int newc, char ynot[])
Process an XML one char at a time.
Definition lilxml.cpp:385
void delXMLEle(XMLEle *ep)
delXMLEle Delete XML element.
Definition lilxml.cpp:167
void delLilXML(LilXML *lp)
Delete a lilxml parser.
Definition lilxml.cpp:159
A little DOM-style library to handle parsing and processing an XML file.
char ** devices
char * vec