Instrument Neutral Distributed Interface INDI  2.0.2
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 #define MAXRBUF 2048
51 
52 static void usage(void);
53 static void deferMessage(XMLEle * root);
54 static void handlePingReply(XMLEle * root);
55 
56 static LilXML *clixml = NULL;
57 
58 #define PROCEED_IMMEDIATE 1
59 #define PROCEED_DEFERRED 0
60 static int messageHandling = PROCEED_IMMEDIATE;
61 
62 
63 /* callback when INDI client message arrives on stdin.
64  * collect and dispatch when see outter element closure.
65  * exit if OS trouble or see incompatable INDI version.
66  * arg is not used.
67  */
68 static void clientMsgCB(int fd, void *arg)
69 {
70  char buf[MAXRBUF], msg[MAXRBUF], *bp;
71  int nr;
72 
73  (void) arg;
74 
75  // FIXME: not ready for receiving shared buffer blobs here
76  // Also this is completely similar to indidriver code... Common code ?
77 
78  /* one read */
79  nr = read(fd, buf, sizeof(buf));
80  if (nr < 0)
81  {
82  if ((errno == EAGAIN) || (errno == EINTR))
83  {
84  return;
85  }
86  fprintf(stderr, "%s: %s\n", me, strerror(errno));
87  exit(1);
88  }
89  if (nr == 0)
90  {
91  fprintf(stderr, "%s: EOF\n", me);
92  exit(1);
93  }
94 
95  /* crack and dispatch when complete */
96  for (bp = buf; nr-- > 0; bp++)
97  {
98  XMLEle *root = readXMLEle(clixml, *bp, msg);
99  if (root)
100  {
101  if (strcmp(tagXMLEle(root), "pingReply") == 0)
102  {
103  handlePingReply(root);
104  delXMLEle(root);
105  continue;
106  }
107  deferMessage(root);
108  }
109  else if (msg[0])
110  fprintf(stderr, "%s XML error: %s\n", me, msg);
111  }
112 }
113 
114 typedef struct DeferredMessage
115 {
120 
121 // Messages will accumulate here until beeing processed
122 static DeferredMessage * firstDeferredMessage = NULL;
123 static DeferredMessage * lastDeferredMessage = NULL;
124 
125 static void flushDeferredMessages(void * arg)
126 {
127  DeferredMessage * p;
128  char msg[MAXRBUF];
129 
130  (void) arg;
131 
132  while((p = firstDeferredMessage))
133  {
134  firstDeferredMessage = p->next;
135  if (firstDeferredMessage)
136  {
137  firstDeferredMessage->prev = NULL;
138  }
139  else
140  {
141  lastDeferredMessage = NULL;
142  }
143 
144  if (dispatch(p->root, msg) < 0)
145  fprintf(stderr, "%s dispatch error: %s\n", me, msg);
146 
147  delXMLEle(p->root);
148  free(p);
149  }
150 }
151 
152 static void deferMessage(XMLEle * root)
153 {
154  if (firstDeferredMessage == NULL)
155  {
156  addImmediateWork(flushDeferredMessages, NULL);
157  }
158 
159  DeferredMessage * newDeferredMessage = (DeferredMessage *)malloc(sizeof(DeferredMessage));
160  newDeferredMessage->root = root;
161  newDeferredMessage->next = NULL;
162  newDeferredMessage->prev = lastDeferredMessage;
163  if (lastDeferredMessage == NULL)
164  {
165  firstDeferredMessage = newDeferredMessage;
166  }
167  else
168  {
169  lastDeferredMessage->next = newDeferredMessage;
170  }
171  lastDeferredMessage = newDeferredMessage;
172 }
173 
174 #define MAX_PING_UID_LEN 64
175 
176 typedef struct PingReply
177 {
178  struct PingReply * prev;
179  struct PingReply * next;
182 
183 static pthread_t eventLoopThread;
184 static PingReply * firstReceivedPing = NULL;
185 static PingReply * lastReceivedPing = NULL;
186 static pthread_mutex_t pingReplyMutex = PTHREAD_MUTEX_INITIALIZER;
187 static pthread_cond_t pingReplyCond = PTHREAD_COND_INITIALIZER;
188 
189 static void handlePingReply(XMLEle * root)
190 {
191  XMLAtt *uidA = findXMLAtt(root, "uid");
192 
193  if (!uidA)
194  return;
195 
196  char * uid = valuXMLAtt(uidA);
197  if (!uid || !uid[0] || strlen(uid) > MAX_PING_UID_LEN) {
198  return;
199  }
200 
201  PingReply *pr = (PingReply *)malloc(sizeof(PingReply));
202  strncpy(pr->uid, uid, MAX_PING_UID_LEN + 1);
203 
204  pthread_mutex_lock(&pingReplyMutex);
205 
206  pr->next = NULL;
207  pr->prev = lastReceivedPing;
208  if (lastReceivedPing) {
209  lastReceivedPing->next = pr;
210  } else {
211  firstReceivedPing = pr;
212  }
213  lastReceivedPing = pr;
214  pthread_cond_broadcast(&pingReplyCond);
215  pthread_mutex_unlock(&pingReplyMutex);
216 }
217 
218 // This must be called under protection of pingReplyMutex
219 static int consumePingReply(const char * uid) {
220  PingReply * cur = firstReceivedPing;
221  while(cur) {
222  if (!strcmp(cur->uid, uid)) {
223 
224  if (cur->prev) {
225  cur->prev->next = cur->next;
226  } else {
227  firstReceivedPing = cur->next;
228  }
229  if (cur->next) {
230  cur->next->prev = cur->prev;
231  } else {
232  lastReceivedPing = cur->prev;
233  }
234 
235  free(cur);
236  return 1;
237  }
238  cur = cur->next;
239 
240  }
241  return 0;
242 }
243 
244 static void waitPingReplyFromEventLoopThread(const char * uid) {
245  pthread_mutex_lock(&pingReplyMutex);
246  while(!consumePingReply(uid)) {
247  pthread_cond_wait(&pingReplyCond, &pingReplyMutex);
248  }
249  pthread_mutex_unlock(&pingReplyMutex);
250 }
251 
252 static void waitPingReplyFromOtherThread(const char * uid) {
253  int fd = 0;
254  fd_set rfd;
255 
256  messageHandling = PROCEED_DEFERRED;
257  pthread_mutex_lock(&pingReplyMutex);
258  while(!consumePingReply(uid))
259  {
260 
261  pthread_mutex_unlock(&pingReplyMutex);
262 
263  FD_ZERO(&rfd);
264  FD_SET(fd, &rfd);
265 
266  int ns = select(fd + 1, &rfd, NULL, NULL, NULL);
267  if (ns < 0)
268  {
269  perror("select");
270  exit(1);
271  }
272 
273  clientMsgCB(0, NULL);
274 
275  pthread_mutex_lock(&pingReplyMutex);
276  }
277  pthread_mutex_unlock(&pingReplyMutex);
278  messageHandling = PROCEED_IMMEDIATE;
279 }
280 
281 void waitPingReply(const char * uid) {
282  // Check if same thread than eventloop
283 
284  pthread_t currentThread = pthread_self();
285  if (!pthread_equal(currentThread, eventLoopThread)) {
286  waitPingReplyFromOtherThread(uid);
287  } else {
288  waitPingReplyFromEventLoopThread(uid);
289  }
290 }
291 
292 int main(int ac, char *av[])
293 {
294 #ifndef _WIN32
295  int ret = 0;
296 
297  if ( (ret = setgid(getgid())) != 0)
298  IDLog("setgid: %s", strerror(ret));
299 
300  if ( (ret = setuid(getuid())) != 0)
301  IDLog("getuid: %s", strerror(ret));
302 
303  if (geteuid() != getuid())
304  exit(255);
305 #endif
306 
307  eventLoopThread = pthread_self();
308 
309  /* save handy pointer to our base name */
310  // #PS: maybe use 'program_invocation_short_name'?
311  for (me = av[0]; av[0][0]; av[0]++)
312  if (av[0][0] == '/')
313  me = &av[0][1];
314 
315  /* crack args */
316  while (--ac && (*++av)[0] == '-')
317  while (*++(*av))
318  switch (*(*av))
319  {
320  case 'v': /* verbose */
321  verbose++;
322  break;
323  default:
324  usage();
325  }
326 
327  /* ac remaining args starting at av[0] */
328  if (ac > 0)
329  usage();
330 
331  /* init */
332  clixml = newLilXML();
333  addCallback(0, clientMsgCB, clixml);
334 
335  /* service client */
336  eventLoop();
337 
338  /* eh?? */
339  fprintf(stderr, "%s: inf loop ended\n", me);
340  return (1);
341 }
342 
343 /* print usage message and exit (1) */
344 static void usage(void)
345 {
346  fprintf(stderr, "Usage: %s [options]\n", me);
347  fprintf(stderr, "Purpose: INDI Device driver framework.\n");
348  fprintf(stderr, "Options:\n");
349  fprintf(stderr, " -v : more verbose to stderr\n");
350 
351  exit(1);
352 }
void eventLoop()
Main calls this when ready to hand over control.
Definition: eventloop.c:105
void addImmediateWork(TCF *fp, void *ud)
Definition: eventloop.c:524
int addCallback(int fd, CBF *fp, void *ud)
Definition: eventloop.c:161
Public interface to INDI's eventloop mechanism.
int errno
void IDLog(const char *fmt,...)
Definition: indicom.c:316
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
struct PingReply PingReply
void waitPingReply(const char *uid)
#define MAXRBUF
#define PROCEED_DEFERRED
struct DeferredMessage DeferredMessage
#define MAX_PING_UID_LEN
#define PROCEED_IMMEDIATE
int main(int ac, char *av[])
int fd
Definition: intelliscope.c:43
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
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
char * valuXMLAtt(XMLAtt *ap)
Return the value of an XML attribute.
Definition: lilxml.cpp:624
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