Instrument Neutral Distributed Interface INDI 2.2.5
indidrivermain.c
Go to the documentation of this file.
1#if 0
2 INDI
3 Copyright (C) 2003-2006 Elwood C. Downey
4
5 Updated by Jasem Mutlaq (2003-2010)
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
21#endif
22
23/* main() for one INDI driver process.
24 * Drivers define IS*() functions we call to deliver INDI XML arriving on stdin.
25 * Drivers call ID*() functions to send INDI XML commands to stdout.
26 * Drivers call IE*() functions to build an event-driver program.
27 * Drivers call IU*() functions to perform various common utility tasks.
28 * Troubles are reported on stderr then we exit.
29 *
30 * This requires liblilxml.
31 */
32
33#include "base64.h"
34#include "eventloop.h"
35#include "indidevapi.h"
36#include "indidriver.h"
37#include "lilxml.h"
38
39#include <errno.h>
40#include <stdarg.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <time.h>
45#include <unistd.h>
46#include <sys/types.h>
47#include <sys/stat.h>
48#include <pthread.h>
49
50#if defined(_WIN32) || defined(__CYGWIN__)
51#include <sys/select.h>
52#endif
53
54#define MAXRBUF 2048
55
56static void usage(void);
57static void deferMessage(XMLEle * root);
58static void handlePingReply(XMLEle * root);
59
60static LilXML *clixml = NULL;
61
62#define PROCEED_IMMEDIATE 1
63#define PROCEED_DEFERRED 0
64static int messageHandling = PROCEED_IMMEDIATE;
65
66
67/* callback when INDI client message arrives on stdin.
68 * collect and dispatch when see outer element closure.
69 * exit if OS trouble or see incompatible INDI version.
70 * arg is not used.
71 */
72static void clientMsgCB(int fd, void *arg)
73{
74 char buf[MAXRBUF], msg[MAXRBUF], *bp;
75 int nr;
76
77 (void) arg;
78
79 // FIXME: not ready for receiving shared buffer blobs here
80 // Also this is completely similar to indidriver code... Common code ?
81
82 /* one read */
83 nr = read(fd, buf, sizeof(buf));
84 if (nr < 0)
85 {
86 if ((errno == EAGAIN) || (errno == EINTR))
87 {
88 return;
89 }
90 fprintf(stderr, "%s: %s\n", me, strerror(errno));
91 exit(1);
92 }
93 if (nr == 0)
94 {
95 fprintf(stderr, "%s: EOF\n", me);
96 exit(1);
97 }
98
99 /* crack and dispatch when complete */
100 for (bp = buf; nr-- > 0; bp++)
101 {
102 XMLEle *root = readXMLEle(clixml, *bp, msg);
103 if (root)
104 {
105 if (strcmp(tagXMLEle(root), "pingReply") == 0)
106 {
107 handlePingReply(root);
108 delXMLEle(root);
109 continue;
110 }
111 deferMessage(root);
112 }
113 else if (msg[0])
114 fprintf(stderr, "%s XML error: %s\n", me, msg);
115 }
116}
117
124
125// Messages will accumulate here until being processed
126static DeferredMessage * firstDeferredMessage = NULL;
127static DeferredMessage * lastDeferredMessage = NULL;
128
129static void flushDeferredMessages(void * arg)
130{
131 DeferredMessage * p;
132 char msg[MAXRBUF];
133
134 (void) arg;
135
136 while((p = firstDeferredMessage))
137 {
138 firstDeferredMessage = p->next;
139 if (firstDeferredMessage)
140 {
141 firstDeferredMessage->prev = NULL;
142 }
143 else
144 {
145 lastDeferredMessage = NULL;
146 }
147
148 if (dispatch(p->root, msg) < 0)
149 fprintf(stderr, "%s dispatch error: %s\n", me, msg);
150
151 delXMLEle(p->root);
152 free(p);
153 }
154}
155
156static void deferMessage(XMLEle * root)
157{
158 if (firstDeferredMessage == NULL)
159 {
160 addImmediateWork(flushDeferredMessages, NULL);
161 }
162
163 DeferredMessage * newDeferredMessage = (DeferredMessage *)malloc(sizeof(DeferredMessage));
164 newDeferredMessage->root = root;
165 newDeferredMessage->next = NULL;
166 newDeferredMessage->prev = lastDeferredMessage;
167 if (lastDeferredMessage == NULL)
168 {
169 firstDeferredMessage = newDeferredMessage;
170 }
171 else
172 {
173 lastDeferredMessage->next = newDeferredMessage;
174 }
175 lastDeferredMessage = newDeferredMessage;
176}
177
178#define MAX_PING_UID_LEN 64
179
180typedef struct PingReply
181{
182 struct PingReply * prev;
183 struct PingReply * next;
186
187static pthread_t eventLoopThread;
188static PingReply * firstReceivedPing = NULL;
189static PingReply * lastReceivedPing = NULL;
190static pthread_mutex_t pingReplyMutex = PTHREAD_MUTEX_INITIALIZER;
191static pthread_cond_t pingReplyCond = PTHREAD_COND_INITIALIZER;
192
193static void handlePingReply(XMLEle * root)
194{
195 XMLAtt *uidA = findXMLAtt(root, "uid");
196
197 if (!uidA)
198 return;
199
200 char * uid = valuXMLAtt(uidA);
201 if (!uid || !uid[0] || strlen(uid) > MAX_PING_UID_LEN) {
202 return;
203 }
204
205 PingReply *pr = (PingReply *)malloc(sizeof(PingReply));
206 strncpy(pr->uid, uid, MAX_PING_UID_LEN + 1);
207
208 pthread_mutex_lock(&pingReplyMutex);
209
210 pr->next = NULL;
211 pr->prev = lastReceivedPing;
212 if (lastReceivedPing) {
213 lastReceivedPing->next = pr;
214 } else {
215 firstReceivedPing = pr;
216 }
217 lastReceivedPing = pr;
218 pthread_cond_broadcast(&pingReplyCond);
219 pthread_mutex_unlock(&pingReplyMutex);
220}
221
222// This must be called under protection of pingReplyMutex
223static int consumePingReply(const char * uid) {
224 PingReply * cur = firstReceivedPing;
225 while(cur) {
226 if (!strcmp(cur->uid, uid)) {
227
228 if (cur->prev) {
229 cur->prev->next = cur->next;
230 } else {
231 firstReceivedPing = cur->next;
232 }
233 if (cur->next) {
234 cur->next->prev = cur->prev;
235 } else {
236 lastReceivedPing = cur->prev;
237 }
238
239 free(cur);
240 return 1;
241 }
242 cur = cur->next;
243
244 }
245 return 0;
246}
247
248static void waitPingReplyFromEventLoopThread(const char * uid) {
249 pthread_mutex_lock(&pingReplyMutex);
250 while(!consumePingReply(uid)) {
251 pthread_cond_wait(&pingReplyCond, &pingReplyMutex);
252 }
253 pthread_mutex_unlock(&pingReplyMutex);
254}
255
256static void waitPingReplyFromOtherThread(const char * uid) {
257 struct timespec startTime, currentTime;
258 const int maxWaitSeconds = 5;
259
260 clock_gettime(CLOCK_MONOTONIC, &startTime);
261
262 messageHandling = PROCEED_DEFERRED;
263 pthread_mutex_lock(&pingReplyMutex);
264 while(!consumePingReply(uid))
265 {
266 // Check total elapsed time — break if we've been waiting too long.
267 // Do NOT call clientMsgCB here — reading from fd 0 in a worker thread
268 // races with the event loop thread also reading from the same fd,
269 // causing split reads and XML parse corruption.
270 clock_gettime(CLOCK_MONOTONIC, &currentTime);
271 double elapsed = (currentTime.tv_sec - startTime.tv_sec)
272 + (currentTime.tv_nsec - startTime.tv_nsec) / 1e9;
273 if (elapsed >= maxWaitSeconds)
274 {
275 fprintf(stderr, "waitPingReply: timeout (%.1fs) waiting for ack of %s, proceeding\n", elapsed, uid);
276 pthread_mutex_unlock(&pingReplyMutex);
277 messageHandling = PROCEED_IMMEDIATE;
278 return;
279 }
280
281 // Wait on condvar with timeout instead of reading from fd
282 struct timespec absTimeout;
283 clock_gettime(CLOCK_REALTIME, &absTimeout);
284 absTimeout.tv_sec += 1;
285 pthread_cond_timedwait(&pingReplyCond, &pingReplyMutex, &absTimeout);
286 }
287 pthread_mutex_unlock(&pingReplyMutex);
288 messageHandling = PROCEED_IMMEDIATE;
289}
290
291void waitPingReply(const char * uid) {
292 // Check if same thread than eventloop
293
294 pthread_t currentThread = pthread_self();
295 if (!pthread_equal(currentThread, eventLoopThread)) {
296 waitPingReplyFromOtherThread(uid);
297 } else {
298 waitPingReplyFromEventLoopThread(uid);
299 }
300}
301
302int main(int ac, char *av[])
303{
304#ifndef _WIN32
305 int ret = 0;
306
307 if ( (ret = setgid(getgid())) != 0)
308 IDLog("setgid: %s", strerror(ret));
309
310 if ( (ret = setuid(getuid())) != 0)
311 IDLog("getuid: %s", strerror(ret));
312
313 if (geteuid() != getuid())
314 exit(255);
315#endif
316
317 eventLoopThread = pthread_self();
318
319 /* save handy pointer to our base name */
320 // #PS: maybe use 'program_invocation_short_name'?
321 for (me = av[0]; av[0][0]; av[0]++)
322 if (av[0][0] == '/')
323 me = &av[0][1];
324
325 /* crack args */
326 while (--ac && (*++av)[0] == '-')
327 while (*++(*av))
328 switch (*(*av))
329 {
330 case 'v': /* verbose */
331 verbose++;
332 break;
333 default:
334 usage();
335 }
336
337 /* ac remaining args starting at av[0] */
338 if (ac > 0)
339 usage();
340
341 /* init */
342 clixml = newLilXML();
343 addCallback(0, clientMsgCB, clixml);
344
345 /* service client */
346 eventLoop();
347
348 /* eh?? */
349 fprintf(stderr, "%s: inf loop ended\n", me);
350 return (1);
351}
352
353/* print usage message and exit (1) */
354static void usage(void)
355{
356 fprintf(stderr, "Usage: %s [options]\n", me);
357 fprintf(stderr, "Purpose: INDI Device driver framework.\n");
358 fprintf(stderr, "Options:\n");
359 fprintf(stderr, " -v : more verbose to stderr\n");
360
361 exit(1);
362}
void eventLoop()
Main calls this when ready to hand over control.
Definition eventloop.c:109
void addImmediateWork(TCF *fp, void *ud)
Definition eventloop.c:528
int addCallback(int fd, CBF *fp, void *ud)
Definition eventloop.c:165
Public interface to INDI's eventloop mechanism.
int main()
int errno
void IDLog(const char *fmt,...)
Definition indicom.c:305
Interface to the reference INDI C API device implementation on the Device Driver side.
int verbose
Definition indidriver.c:49
int dispatch(XMLEle *root, char msg[])
Definition indidriver.c:180
char * me
Definition indidriver.c:50
void waitPingReply(const char *uid)
#define MAXRBUF
#define PROCEED_DEFERRED
#define MAX_PING_UID_LEN
#define PROCEED_IMMEDIATE
void usage(void)
int fd
XMLAtt * findXMLAtt(XMLEle *ep, const char *name)
Find an XML attribute within an XML element.
Definition lilxml.cpp:524
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
char * valuXMLAtt(XMLAtt *ap)
Return the value of an XML attribute.
Definition lilxml.cpp:624
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
A little DOM-style library to handle parsing and processing an XML file.
Namespace to encapsulate INDI client, drivers, and mediator classes.
struct DeferredMessage * next
struct DeferredMessage * prev
struct PingReply * next
char uid[MAX_PING_UID_LEN+1]
struct PingReply * prev