Instrument Neutral Distributed Interface INDI 2.2.5
indiserver.cpp
Go to the documentation of this file.
1/* INDI Server for protocol version 1.7.
2 * Copyright (C) 2007 Elwood C. Downey <[email protected]>
3 2013 Jasem Mutlaq <[email protected]>
4 2022 Ludovic Pollet
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
19 * argv lists names of Driver programs to run or sockets to connect for Devices.
20 * Drivers are restarted if they exit or connection closes.
21 * Each local Driver's stdin/out are assumed to provide INDI traffic and are
22 * connected here via pipes. Local Drivers' stderr are connected to our
23 * stderr with date stamp and driver name prepended.
24 * We only support Drivers that advertise support for one Device. The problem
25 * with multiple Devices in one Driver is without a way to know what they
26 * _all_ are there is no way to avoid sending all messages to all Drivers.
27 * Outbound messages are limited to Devices and Properties seen inbound.
28 * Messages to Devices on sockets always include Device so the chained
29 * indiserver will only pass back info from that Device.
30 * All newXXX() received from one Client are echoed to all other Clients who
31 * have shown an interest in the same Device and property.
32 *
33 * 2017-01-29 JM: Added option to drop stream blobs if client blob queue is
34 * higher than maxstreamsiz bytes
35 *
36 * Implementation notes:
37 *
38 * We fork each driver and open a server socket listening for INDI clients.
39 * Then forever we listen for new clients and pass traffic between clients and
40 * drivers, subject to optimizations based on sniffing messages for matching
41 * Devices and Properties. Since one message might be destined to more than
42 * one client or device, they are queued and only removed after the last
43 * consumer is finished. XMLEle are converted to linear strings before being
44 * sent to optimize write system calls and avoid blocking to slow clients.
45 * Clients that get more than maxqsiz bytes behind are shut down.
46 */
47#ifndef _GNU_SOURCE
48#define _GNU_SOURCE // needed for siginfo_t and sigaction
49#endif
50
51#include "Fifo.hpp"
52#include "ClInfo.hpp"
53#include "DvrInfo.hpp"
54#include "LocalDrvInfo.hpp"
55#include "RemoteDvrInfo.hpp"
56#include "TcpServer.hpp"
57#include "UnixServer.hpp"
58#include "Utils.hpp"
59#include "Constants.hpp"
60#include "CommandLineArgs.hpp"
61
62#include "config.h"
63#include <string>
64
65#include <assert.h>
66
67#include "indiapi.h"
68
69#include <memory>
70#include <fcntl.h>
71#include <libgen.h>
72#include <netdb.h>
73#include <stdio.h>
74#include <stdlib.h>
75#include <stdarg.h>
76#include <stdint.h>
77#include <poll.h>
78#include <unistd.h>
79#include <arpa/inet.h>
80#include <netinet/in.h>
81#include <sys/time.h>
82#include <sys/ioctl.h>
83#include <sys/types.h>
84#include <sys/wait.h>
85#include <sys/stat.h>
86#include <sys/socket.h>
87#include <sys/mman.h>
88#include <unistd.h>
89#include <sys/un.h>
90#ifdef MSG_ERRQUEUE
91#include <linux/errqueue.h>
92#endif
93
94#include <ev++.h>
95using namespace indiserver::constants;
96
97static ev::default_loop loop;
98
100Fifo* fifoHandle{nullptr};
101
102/* print usage message and exit (2) */
103void usage(void)
104{
105 fprintf(stderr, "Usage: %s [options] driver [driver ...]\n", userConfigurableArguments->binaryName.c_str());
106 fprintf(stderr, "Purpose: server for local and remote INDI drivers\n");
107 fprintf(stderr, "INDI Library: %s\nCode %s. Protocol %g.\n", CMAKE_INDI_VERSION_STRING, GIT_TAG_STRING, INDIV);
108 fprintf(stderr, "Options:\n");
109 fprintf(stderr, " -l d : log driver messages to <d>/YYYY-MM-DD.islog\n");
110 fprintf(stderr, " -m m : kill client if gets more than this many MB behind, default %d\n", defaultMaxQueueSizeMB);
111 fprintf(stderr,
112 " -d m : drop streaming blobs if client gets more than this many MB behind, default %d. 0 to disable\n",
114#ifdef ENABLE_INDI_SHARED_MEMORY
115 fprintf(stderr, " -u path : Path for the local connection socket (abstract), default %s\n", INDIUNIXSOCK);
116#endif
117 fprintf(stderr, " -p p : alternate IP port, default %d\n", indiPortDefault);
118 fprintf(stderr, " -r r : maximum driver restarts on error, default %d\n", defaultMaximumRestarts);
119 fprintf(stderr, " -f path : Path to fifo for dynamic startup and shutdown of drivers.\n");
120 fprintf(stderr, " -v : show key events, no traffic\n");
121 fprintf(stderr, " -vv : -v + key message content\n");
122 fprintf(stderr, " -vvv : -vv + complete xml\n");
123 fprintf(stderr, "driver : executable or [device]@host[:port]\n");
124
125 exit(2);
126}
127
128int main(int ac, char *av[])
129{
130 /* log startup */
131 logStartup(ac, av);
132
133 std::unique_ptr<CommandLineArgs> argValues = std::make_unique<CommandLineArgs>();
134 userConfigurableArguments = argValues.get();
135
136 std::unique_ptr<Fifo> fifoHandleOwner{};
137
138 /* save our name */
139 argValues->binaryName = av[0];
140
141#ifdef OSX_EMBEDED_MODE
142
143 char logname[128];
144 // logNamePattern is std::string_view, use .data() for the printf format arg
145 snprintf(logname, 128, logNamePattern.data(), getlogin());
146 fprintf(stderr, "switching stderr to %s", logname);
147 freopen(logname, "w", stderr);
148
149 // Compute the FIFO path from TMPDIR so that on a sandboxed macOS build the
150 // file is created inside the app-container private temp directory instead of
151 // /private/tmp/. Falls back to /tmp on non-sandboxed / Linux builds.
152 {
153 const char *tmpDir = getenv("TMPDIR");
154 if (!tmpDir || tmpDir[0] == '\0')
155 tmpDir = "/tmp";
156 std::string fifoPath = tmpDir;
157 if (fifoPath.back() != '/')
158 fifoPath += '/';
159 fifoPath += "indiserverFIFO";
160 fifoHandleOwner = std::make_unique<Fifo>(fifoPath);
161 }
162 fifoHandle = fifoHandleOwner.get();
163 // Preserve original behaviour: verbose mode and no command-line drivers
165 ac = 0;
166
167#else
168
169 /* crack args */
170 while ((--ac > 0) && ((*++av)[0] == '-'))
171 {
172 char *s;
173 for (s = av[0] + 1; *s != '\0'; s++)
174 switch (*s)
175 {
176 case 'l':
177 if (ac < 2)
178 {
179 fprintf(stderr, "-l requires log directory\n");
180 usage();
181 }
183 ac--;
184 break;
185 case 'm':
186 if (ac < 2)
187 {
188 fprintf(stderr, "-m requires max MB behind\n");
189 usage();
190 }
191 userConfigurableArguments->maxQueueSizeMB = 1024 * 1024 * atoi(*++av);
192 ac--;
193 break;
194 case 'p':
195 if (ac < 2)
196 {
197 fprintf(stderr, "-p requires port value\n");
198 usage();
199 }
200 userConfigurableArguments->port = atoi(*++av);
201 ac--;
202 break;
203 case 'd':
204 if (ac < 2)
205 {
206 fprintf(stderr, "-d requires max stream MB behind\n");
207 usage();
208 }
209 userConfigurableArguments->maxStreamSizeMB = 1024 * 1024 * atoi(*++av);
210 ac--;
211 break;
212#ifdef ENABLE_INDI_SHARED_MEMORY
213 case 'u':
214 if (ac < 2)
215 {
216 fprintf(stderr, "-u requires local socket path\n");
217 usage();
218 }
219 UnixServer::unixSocketPath = *++av;
220 ac--;
221 break;
222#endif // ENABLE_INDI_SHARED_MEMORY
223 case 'f':
224 if (ac < 2)
225 {
226 fprintf(stderr, "-f requires fifo node\n");
227 usage();
228 }
229 assert(!fifoHandle);
230 fifoHandleOwner = std::make_unique<Fifo>(*++av);
231 fifoHandle = fifoHandleOwner.get();
232 ac--;
233 break;
234 case 'r':
235 if (ac < 2)
236 {
237 fprintf(stderr, "-r requires number of restarts\n");
238 usage();
239 }
243 ac--;
244 break;
245 case 'v':
247 break;
248 default:
249 usage();
250 }
251 }
252#endif
253
254 /* at this point there are ac args in av[] to name our drivers */
255 if (ac == 0 && !fifoHandle)
256 usage();
257
258 /* take care of some unixisms */
259 noSIGPIPE();
260
261 std::vector<std::unique_ptr<DvrInfo>> drivers(ac);
262
263 /* start each driver */
264 while (ac-- > 0)
265 {
266 std::string dvrName = *av++;
267 if (dvrName.find('@') != std::string::npos)
268 {
269 drivers.push_back(std::make_unique<RemoteDvrInfo>());
270 }
271 else
272 {
273 drivers.push_back(std::make_unique<LocalDvrInfo>());
274 }
275 drivers.back()->name = dvrName;
276 drivers.back()->start();
277 }
278
279 /* announce we are online */
280 const auto tcpServer = std::make_unique<TcpServer>(userConfigurableArguments->port);
281 tcpServer->listen();
282
283#ifdef ENABLE_INDI_SHARED_MEMORY
284 /* create a new unix server */
285 const auto unixServer = std::make_unique<UnixServer>(UnixServer::unixSocketPath);
286 unixServer->listen();
287#endif
288 /* Load up FIFO, if available */
289 if (fifoHandle)
290 {
291 // New started drivers will not inherit server's prefix anymore
292
293 // JM 2022.07.23: This causes an issue on MacOS. Disabled for now until investigated further.
294 //unsetenv("INDIPREFIX");
296 }
297
298 /* handle new clients and all io */
299 loop.loop();
300
301 /* will not happen unless no more listener left ! */
302 log("unexpected return from event loop\n");
303 return (1);
304}
305
void log(const std::string &log)
Definition Utils.cpp:161
void noSIGPIPE()
Definition Utils.cpp:48
void logStartup(int ac, char *av[])
Definition Utils.cpp:33
Definition Fifo.hpp:25
void listen()
Definition Fifo.hpp:42
int main()
Constants and Data structure definitions for the interface to the reference INDI C API implementation...
#define INDIV
Definition indiapi.h:134
CommandLineArgs * userConfigurableArguments
Fifo * fifoHandle
void usage(void)
constexpr unsigned defaultMaxQueueSizeMB
Definition Constants.hpp:25
constexpr unsigned defaultMaximumRestarts
Definition Constants.hpp:27
constexpr unsigned indiPortDefault
Definition Constants.hpp:23
constexpr unsigned defaultMaxStreamSizeMB
Definition Constants.hpp:26
unsigned int maxStreamSizeMB
unsigned int maxQueueSizeMB
std::string binaryName