Instrument Neutral Distributed Interface INDI  2.0.2
indidriverio.c
Go to the documentation of this file.
1 #if 0
2 INDI Driver Functions
3 
4 Copyright (C) 2022 by Ludovic Pollet
5 
6 This library is free software;
7 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;
10 either
11 version 2.1 of the License, or (at your option) any later version.
12 
13 This library is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY;
15 without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
18 
19 You should have received a copy of the GNU Lesser General Public
20 License along with this library;
21 if not, write to the Free Software
22 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110 - 1301 USA
23 
24 #endif
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <sys/un.h>
34 #include <errno.h>
35 #include <pthread.h>
36 
37 #include "indidriver.h"
38 #include "userio.h"
39 #include "indiuserio.h"
40 #include "indidriverio.h"
41 
42 
43 
44 /* Buffer size. Must be ^ 2 */
45 #define OUTPUTBUFF_ALLOC 32768
46 
47 /* Dump whole buffer when growing over this */
48 #define OUTPUTBUFF_FLUSH_THRESOLD 65536
49 
50 #define MAXFD_PER_MESSAGE 16
51 
52 static void driverio_flush(driverio * dio, const void * additional, size_t add_size);
53 
54 static pthread_mutex_t stdout_mutex = PTHREAD_MUTEX_INITIALIZER;
55 
56 /* Return the buffer size required for storage (rounded to next OUTPUTBUFF_ALLOC) */
57 static unsigned int outBuffRequired(unsigned int storage)
58 {
59  return (storage + OUTPUTBUFF_ALLOC - 1) & ~(OUTPUTBUFF_ALLOC - 1);
60 }
61 
62 static int outBuffAllocated(struct driverio * dio)
63 {
64  return outBuffRequired(dio->outPos);
65 }
66 
67 static void outBuffGrow(struct driverio * dio, int required)
68 {
69  dio->outBuff = realloc(dio->outBuff, required);
70  if (dio->outBuff == NULL)
71  {
72  perror("malloc");
73  _exit(1);
74  }
75 }
76 
77 static ssize_t driverio_write(void *user, const void * ptr, size_t count)
78 {
79  struct driverio * dio = (struct driverio*) user;
80 
81  if (dio->outPos + count > OUTPUTBUFF_FLUSH_THRESOLD)
82  {
83  driverio_flush(dio, ptr, count);
84  }
85  else
86  {
87  unsigned int allocated = outBuffAllocated(dio);
88  unsigned int required = outBuffRequired(dio->outPos + count);
89  if (required != allocated)
90  {
91  outBuffGrow(dio, required);
92  }
93  memcpy(dio->outBuff + dio->outPos, ptr, count);
94 
95  dio->outPos += count;
96  }
97  return count;
98 }
99 
100 static int driverio_vprintf(void *user, const char * fmt, va_list arg)
101 {
102  struct driverio * dio = (struct driverio*) user;
103  int available;
104  int size = 0;
105 
106  unsigned int allocated = outBuffAllocated(dio);
107  while(1)
108  {
109  available = allocated - dio->outPos;
110  /* Determine required size */
111  size = vsnprintf(dio->outBuff + dio->outPos, available, fmt, arg);
112 
113  if (size < 0)
114  return size;
115 
116  if (size < available)
117  {
118  break;
119  }
120  allocated = outBuffRequired(available + size + 1);
121  outBuffGrow(dio, allocated);
122  }
123  dio->outPos += size;
124  return size;
125 }
126 
127 static void driverio_join(void * user, const char * xml, void * blob, size_t bloblen)
128 {
129  struct driverio * dio = (struct driverio*) user;
130  dio->joinCount++;
131  dio->joins = (void **)realloc((void*)dio->joins, sizeof(void*) * dio->joinCount);
132  dio->joinSizes = (size_t *)realloc((void*)dio->joinSizes, sizeof(size_t) * dio->joinCount);
133 
134  dio->joins[dio->joinCount - 1] = blob;
135  dio->joinSizes[dio->joinCount - 1] = bloblen;
136 
137  driverio_write(user, xml, strlen(xml));
138 }
139 
140 
141 static void driverio_flush(driverio * dio, const void * additional, size_t add_size)
142 {
143  struct msghdr msgh;
144  struct iovec iov[2];
145  int cmsghdrlength;
146  struct cmsghdr * cmsgh;
147 
148  if (dio->outPos + add_size)
149  {
150  int ret = -1;
151  void ** temporaryBuffers = NULL;
152  int fdCount = dio->joinCount;
153  if (fdCount > 0)
154  {
155 
156  if (dio->joinCount > MAXFD_PER_MESSAGE)
157  {
158  errno = EMSGSIZE;
159  perror("sendmsg");
160  exit(1);
161  }
162 
163  cmsghdrlength = CMSG_SPACE((fdCount * sizeof(int)));
164  cmsgh = (struct cmsghdr*)malloc(cmsghdrlength);
165  // FIXME: abort on alloc error here
166  temporaryBuffers = (void**)malloc(sizeof(void*)*fdCount);
167 
168  /* Write the fd as ancillary data */
169  cmsgh->cmsg_len = CMSG_LEN(sizeof(int));
170  cmsgh->cmsg_level = SOL_SOCKET;
171  cmsgh->cmsg_type = SCM_RIGHTS;
172  msgh.msg_control = cmsgh;
173  msgh.msg_controllen = cmsghdrlength;
174  for(int i = 0; i < fdCount; ++i)
175  {
176  void * blob = dio->joins[i];
177  size_t size = dio->joinSizes[i];
178 
179  int fd = IDSharedBlobGetFd(blob);
180  if (fd == -1)
181  {
182  // Can't avoid a copy here. Update the driver to change that
183  temporaryBuffers[i] = IDSharedBlobAlloc(size);
184  memcpy(temporaryBuffers[i], blob, size);
185  fd = IDSharedBlobGetFd(temporaryBuffers[i]);
186  }
187  else
188  {
189  temporaryBuffers[i] = NULL;
190  }
191 
192  ((int *) CMSG_DATA(CMSG_FIRSTHDR(&msgh)))[i] = fd;
193  }
194  }
195  else
196  {
197  cmsgh = NULL;
198  cmsghdrlength = 0;
199  msgh.msg_control = cmsgh;
200  msgh.msg_controllen = cmsghdrlength;
201  }
202 
203  iov[0].iov_base = dio->outBuff;
204  iov[0].iov_len = dio->outPos;
205  if (add_size)
206  {
207  iov[1].iov_base = (void*)additional;
208  iov[1].iov_len = add_size;
209  }
210 
211  msgh.msg_flags = 0;
212  msgh.msg_name = NULL;
213  msgh.msg_namelen = 0;
214  msgh.msg_iov = iov;
215  msgh.msg_iovlen = add_size ? 2 : 1;
216 
217  if (!dio->locked)
218  {
219  pthread_mutex_lock(&stdout_mutex);
220  dio->locked = 1;
221  }
222 
223  ret = sendmsg(1, &msgh, 0);
224  if (ret == -1)
225  {
226  perror("sendmsg");
227  // FIXME: exiting the driver seems abrupt. Is this the right thing to do ? what about cleanup ?
228  exit(1);
229  }
230  else if ((unsigned)ret != dio->outPos + add_size)
231  {
232  // This is not expected on blocking socket
233  fprintf(stderr, "short write\n");
234  exit(1);
235  }
236 
237  if (fdCount > 0)
238  {
239  for(int i = 0; i < fdCount; ++i)
240  {
241  if (temporaryBuffers[i] != NULL)
242  {
243  IDSharedBlobFree(temporaryBuffers[i]);
244  }
245  }
246  free(cmsgh);
247  free(temporaryBuffers);
248  }
249  }
250 
251  if (dio->joins != NULL)
252  {
253  free(dio->joins);
254  }
255  dio->joins = NULL;
256 
257  if (dio->joinSizes != NULL)
258  {
259  free(dio->joinSizes);
260  }
261  dio->joinSizes = NULL;
262 
263  if (dio->outBuff != NULL)
264  {
265  free(dio->outBuff);
266  }
267  dio->outPos = 0;
268 
269 }
270 
271 
272 static int driverio_is_unix = -1;
273 
274 static int is_unix_io()
275 {
276 #ifndef ENABLE_INDI_SHARED_MEMORY
277  return 0;
278 #endif
279  if (driverio_is_unix != -1)
280  {
281  return driverio_is_unix;
282  }
283 
284 #ifdef SO_DOMAIN
285  int domain;
286  socklen_t result = sizeof(domain);
287 
288  if (getsockopt(1, SOL_SOCKET, SO_DOMAIN, (void*)&domain, &result) == -1)
289  {
290  driverio_is_unix = 0;
291  }
292  else if (result != sizeof(domain) || domain != AF_UNIX)
293  {
294  driverio_is_unix = 0;
295  }
296  else
297  {
298  driverio_is_unix = 1;
299  }
300 #else
301  struct sockaddr_un sockName;
302  socklen_t sockNameLen = sizeof(sockName);
303 
304  if (getsockname(1, (struct sockaddr*)&sockName, &sockNameLen) == -1)
305  {
306  driverio_is_unix = 0;
307  }
308  else if (sockName.sun_family == AF_UNIX)
309  {
310  driverio_is_unix = 1;
311  }
312  else
313  {
314  driverio_is_unix = 0;
315  }
316 #endif
317  return driverio_is_unix;
318 }
319 
320 /* Unix io allow attaching buffer in ancillary data. */
321 static void driverio_init_unix(driverio * dio)
322 {
323  dio->userio.vprintf = &driverio_vprintf;
324  dio->userio.write = &driverio_write;
325  dio->userio.joinbuff = &driverio_join;
326  dio->user = (void*)dio;
327  dio->joins = NULL;
328  dio->joinSizes = NULL;
329  dio->locked = 0;
330  dio->joinCount = 0;
331  dio->outBuff = NULL;
332  dio->outPos = 0;
333 }
334 
335 static void driverio_finish_unix(driverio * dio)
336 {
337  driverio_flush(dio, NULL, 0);
338  if (dio->locked)
339  {
340  pthread_mutex_unlock(&stdout_mutex);
341  dio->locked = 0;
342  }
343 }
344 
345 static void driverio_init_stdout(driverio * dio)
346 {
347  dio->userio = *userio_file();
348  dio->user = stdout;
349  pthread_mutex_lock(&stdout_mutex);
350 }
351 
352 static void driverio_finish_stdout(driverio * dio)
353 {
354  (void)dio;
355  fflush(stdout);
356  pthread_mutex_unlock(&stdout_mutex);
357 }
358 
360 {
361  if (is_unix_io())
362  {
363  driverio_init_unix(dio);
364  }
365  else
366  {
367  driverio_init_stdout(dio);
368  }
369 }
370 
372 {
373  if (is_unix_io())
374  {
375  driverio_finish_unix(dio);
376  }
377  else
378  {
379  driverio_finish_stdout(dio);
380  }
381 }
int errno
void driverio_init(driverio *dio)
Definition: indidriverio.c:359
#define OUTPUTBUFF_FLUSH_THRESOLD
Definition: indidriverio.c:48
void driverio_finish(driverio *dio)
Definition: indidriverio.c:371
#define OUTPUTBUFF_ALLOC
Definition: indidriverio.c:45
#define MAXFD_PER_MESSAGE
Definition: indidriverio.c:50
int fd
Definition: intelliscope.c:43
Namespace to encapsulate INDI client, drivers, and mediator classes.
void IDSharedBlobFree(void *ptr)
Definition: sharedblob.c:132
void * IDSharedBlobAlloc(size_t size)
Definition: sharedblob.c:70
int IDSharedBlobGetFd(void *ptr)
Definition: sharedblob.c:241
void ** joins
Definition: indidriverio.h:31
char * outBuff
Definition: indidriverio.h:35
void * user
Definition: indidriverio.h:30
unsigned int outPos
Definition: indidriverio.h:36
struct userio userio
Definition: indidriverio.h:29
int locked
Definition: indidriverio.h:34
size_t * joinSizes
Definition: indidriverio.h:32
int joinCount
Definition: indidriverio.h:33
void(* joinbuff)(void *user, const char *xml, void *buffer, size_t bloblen)
Definition: userio.h:34
int(* vprintf)(void *user, const char *format, va_list arg)
Definition: userio.h:31
ssize_t(* write)(void *user, const void *ptr, size_t count)
Definition: userio.h:30
const struct userio * userio_file()
Definition: userio.c:39