Line data Source code
1 : /// $Id: IndiConnection.cpp
2 : ///
3 : /// @author Paul Grenz
4 : ///
5 : ////////////////////////////////////////////////////////////////////////////////
6 :
7 : #include <stdlib.h>
8 : #include <iostream>
9 : #include <signal.h>
10 : #include <errno.h>
11 : #include <unistd.h>
12 : #include <stdexcept>
13 : #include <sys/types.h> // provides 'umask'
14 : #include <sys/stat.h> // provides 'umask'
15 : #include <sys/time.h> // provides 'setrlimit'
16 : #include <sys/resource.h> // provides 'setrlimit'
17 : #include "IndiConnection.hpp"
18 : #include "TimeStamp.hpp"
19 :
20 : using std::exception;
21 : using std::runtime_error;
22 : using std::string;
23 : using std::vector;
24 : using std::stringstream;
25 : using std::endl;
26 : using pcf::TimeStamp;
27 : using pcf::IndiConnection;
28 : using pcf::IndiXmlParser;
29 : using pcf::IndiMessage;
30 : using pcf::IndiProperty;
31 :
32 :
33 : ////////////////////////////////////////////////////////////////////////////////
34 : /// \brief IndiConnection::IndiConnection
35 : /// Standard constructor.
36 :
37 0 : IndiConnection::IndiConnection()
38 : {
39 0 : construct( "generic_indi_process", "1", "1" );
40 0 : }
41 :
42 : ////////////////////////////////////////////////////////////////////////////////
43 : /// \brief IndiConnection::IndiConnection
44 : /// Constructor which sets the name, version, and INDI protocol version.
45 : /// \param szName Name of this object.
46 : /// \param szVersion Version of this object.
47 : /// \param szProtocolVersion INDI protocol version.
48 :
49 12 : IndiConnection::IndiConnection( const string &szName,
50 : const string &szVersion,
51 36 : const string &szProtocolVersion )
52 : {
53 12 : construct( szName, szVersion, szProtocolVersion );
54 12 : }
55 :
56 : ////////////////////////////////////////////////////////////////////////////////
57 : /// \brief IndiConnection::IndiConnection
58 : /// Copy constructor.
59 : /// \param idRhs Another version of the driver.
60 :
61 0 : IndiConnection::IndiConnection( const IndiConnection &idRhs ) : Thread()
62 : {
63 : static_cast<void>(idRhs);
64 : // Empty because this is private.
65 0 : }
66 :
67 : ////////////////////////////////////////////////////////////////////////////////
68 : /// \brief IndiConnection::operator =
69 : /// Assignment operator.
70 : /// \param idRhs The right-hand side of the operation.
71 : /// \return This object.
72 :
73 0 : const IndiConnection &IndiConnection::operator= ( const IndiConnection &idRhs )
74 : {
75 : static_cast<void>(idRhs);
76 : // Empty because this is private.
77 0 : return *this;
78 : }
79 :
80 : ////////////////////////////////////////////////////////////////////////////////
81 : /// \brief IndiConnection::~IndiConnection
82 : /// Standard destructor.
83 :
84 12 : IndiConnection::~IndiConnection()
85 : {
86 : try
87 : {
88 12 : deactivate();
89 : }
90 0 : catch(...)
91 : {
92 : //do nothing
93 0 : }
94 :
95 12 : MutexLock::AutoLock autoOut( &m_mutOutput );
96 12 : if(m_fdOutput >= 0 && m_fdOutput != STDOUT_FILENO)
97 : {
98 3 : ::close(m_fdOutput);
99 : }
100 12 : m_fdOutput = -1;
101 :
102 12 : }
103 :
104 : ////////////////////////////////////////////////////////////////////////////////
105 : /// \brief IndiConnection::construct
106 : /// Called from the constructor to initialize member variables.
107 : /// \param szName Name for this INDI process to use.
108 : /// \param szVersion Version of the process.
109 : /// \param szProtocolVersion Version of the INDI protocol.
110 :
111 12 : void IndiConnection::construct( const string &szName,
112 : const string &szVersion,
113 : const string &szProtocolVersion )
114 : {
115 : // Make sure we are in a known state.
116 12 : m_oQuitProcess = false;
117 :
118 : // The process thread has not been set up yet.
119 12 : m_idProcessThread = 0;
120 :
121 : // These are the two descriptors we will use to talk to the outside world.
122 12 : m_fdInput = STDIN_FILENO;
123 :
124 : // We start with STDOUT.
125 12 : m_fdOutput = STDOUT_FILENO;
126 :
127 : // setup the signal handler.
128 : //::signal( SIGHUP, IndiConnection::handleSignal );
129 : //::signal( SIGINT, IndiConnection::handleSignal );
130 : //::signal( SIGTERM, IndiConnection::handleSignal );
131 :
132 : // Set our information that sets us up as a unique INDI component.
133 12 : setName( szName );
134 12 : setVersion( szVersion );
135 12 : setProtocolVersion( szProtocolVersion );
136 :
137 12 : m_oIsVerboseModeEnabled = false;
138 :
139 : // What is the interval at which our 'execute' function is called?
140 : // This is the same if we are in simulation mode or not.
141 : // The default is one second.
142 12 : setInterval(1000);
143 :
144 : // What is our CPU affinity? This is the CPU we will run on.
145 : // A -1 indicates we don't care where it runs.
146 12 : m_iCpuAffinity = -1;
147 :
148 : // allocate a big buffer to hold the input data.
149 12 : m_vecInputBuf = vector<unsigned char>( InputBufSize );
150 :
151 12 : }
152 :
153 : ////////////////////////////////////////////////////////////////////////////////
154 : /// \brief IndiConnection::beforeExecute
155 : /// Override this function to do something before this device has been told to
156 : /// start running the thread, like allocate memory.
157 :
158 0 : void IndiConnection::beforeExecute()
159 : {
160 0 : }
161 :
162 : ////////////////////////////////////////////////////////////////////////////////
163 : /// \brief IndiConnection::afterExecute
164 : /// Override this function to do something after this device has been told to
165 : /// stop running the thread, like clean up allocated memory.
166 :
167 0 : void IndiConnection::afterExecute()
168 : {
169 0 : }
170 :
171 : ////////////////////////////////////////////////////////////////////////////////
172 : /// \brief IndiConnection::execute
173 : /// Function which executes in a loop in a separate thread.
174 : /// Override in derived class to perform some action.
175 :
176 0 : void IndiConnection::execute()
177 : {
178 0 : }
179 :
180 : ////////////////////////////////////////////////////////////////////////////////
181 : /// \brief IndiConnection::activate
182 : /// Try to start the driver 'execute' thread. If it is already running, this
183 : /// will throw. When the thread starts, it calls 'beforeExecute' before
184 : /// calling 'execute' in a loop.
185 :
186 3 : void IndiConnection::activate()
187 : {
188 : // is the thread already running?
189 3 : if ( isRunning() == true )
190 0 : throw runtime_error( string( "Tried to activate when already active." ) );
191 :
192 : // Start the 'execute' thread running to perform the component.
193 3 : start( m_iCpuAffinity );
194 3 : }
195 :
196 : /////////////////////////////////////////////////////////////////////////////////
197 : /// \brief IndiConnection::deactivate
198 : /// Try to stop the driver 'execute' thread and the 'process' thread. If
199 : /// neither is running, this will have no effect. This will stop the 'execute'
200 : /// being called in a loop and call 'afterExecute' before stopping the thread.
201 :
202 15 : void IndiConnection::deactivate()
203 : {
204 15 : if ( isRunning() == true )
205 : {
206 3 : stop();
207 3 : join();
208 3 : if ( m_idProcessThread != 0 )
209 : {
210 0 : ::pthread_join( m_idProcessThread, NULL );
211 0 : m_idProcessThread = 0;
212 : }
213 : }
214 15 : }
215 :
216 : ////////////////////////////////////////////////////////////////////////////////
217 : /// \brief IndiConnection::isActive
218 : /// Is the driver currently active ('execute' thread running)?
219 : /// \return true or false.
220 :
221 12 : bool IndiConnection::isActive() const
222 : {
223 12 : return isRunning();
224 : }
225 :
226 : ////////////////////////////////////////////////////////////////////////////////
227 : /// \brief IndiConnection::processIndiRequests
228 : /// Called to ensure that incoming INDI messages are received and handled.
229 : /// It will not exit until we receive a signal. May create a new thread.
230 : /// \param oUseThread Run this in a separate thread or not.
231 :
232 3 : void IndiConnection::processIndiRequests( const bool &oUseThread )
233 : {
234 3 : if ( oUseThread == false )
235 : {
236 3 : process();
237 : }
238 : else
239 : {
240 0 : m_idProcessThread = 0;
241 0 : ::pthread_create( &m_idProcessThread, NULL,
242 : IndiConnection::pthreadProcess, this );
243 : }
244 3 : }
245 :
246 : ////////////////////////////////////////////////////////////////////////////////
247 : /// \brief IndiConnection::pthreadProcess
248 : /// 'pthread_create' needs a static function to get the thread going.
249 : /// Passing a pointer back to this class allows us to call the 'runLoop'
250 : /// function from within the new thread.
251 : /// \param pUnknown A pointer to this class that the thread function can use.
252 : /// \return NULL - no return is needed.
253 :
254 0 : void *IndiConnection::pthreadProcess( void *pUnknown )
255 : {
256 : // do a static cast to get the pointer back to a "Thread" object.
257 0 : IndiConnection *pThis = static_cast<IndiConnection *>( pUnknown );
258 :
259 : // we are now within the new thread and up and running.
260 : try
261 : {
262 0 : pThis->process();
263 : }
264 0 : catch ( const std::exception &excep )
265 : {
266 0 : std::cerr << "Process thread exited: " << excep.what() << std::endl;
267 0 : }
268 0 : catch ( ... )
269 : {
270 0 : std::cerr << "An exception was thrown, process thread exited." << std::endl;
271 0 : }
272 :
273 : /// no return is necessary, since it is not examined.
274 0 : return NULL;
275 : }
276 :
277 : ////////////////////////////////////////////////////////////////////////////////
278 : /// \brief IndiConnection::process
279 : /// Listens on the file descriptor for incoming INDI messages. Exits when the
280 : /// 'Quit Process' flag becomes true.
281 :
282 3 : void IndiConnection::process()
283 : {
284 : // Loop here until we are told to quit or we hit an error.
285 6 : while ( m_oQuitProcess == false )
286 : {
287 : try
288 : {
289 : // Call the 'update' function to do something each time we pass through
290 : // this loop reading input.
291 3 : update();
292 :
293 : // The length of the command received.
294 3 : int nInputBufLen = 0;
295 3 : ::memset( &m_vecInputBuf[0], 0, InputBufSize );
296 :
297 : // Create and clear out the FD set.
298 : fd_set fdsRead;
299 51 : FD_ZERO( &fdsRead );
300 : // Watch input to see when we get some input.
301 3 : FD_SET( m_fdInput, &fdsRead );
302 :
303 : // The argument to 'select' must be +1 greater than the largest fd.
304 3 : int nHighestNumberedFd = m_fdInput;
305 :
306 : // Set the timeout on the select call.
307 : timeval tv;
308 3 : tv.tv_sec = 1; //0;
309 3 : tv.tv_usec = 0; //10000;
310 :
311 : // We need a timeout on the select to ensure that we loop around and
312 : // call the 'update' function regularly.
313 3 : int nRetval = ::select( nHighestNumberedFd + 1, &fdsRead, NULL, NULL, &tv );
314 : //int nRetval = ::select( nHighestNumberedFd+1, &fdsRead, NULL, NULL, NULL );
315 :
316 3 : if ( nRetval == -1 )
317 : {
318 3 : if ( m_oQuitProcess == false )
319 : {
320 0 : Thread::sleep( 1 );
321 : }
322 : }
323 0 : else if ( nRetval == 0 )
324 : {
325 : // Timed out - just loop back around.
326 : }
327 : // We must check the input file descriptor.
328 0 : else if ( FD_ISSET( m_fdInput, &fdsRead ) != 0 )
329 : {
330 : // Receive a command
331 0 : nInputBufLen = ::read( m_fdInput, &m_vecInputBuf[0], InputBufSize );
332 0 : if ( nInputBufLen < 0 )
333 : {
334 0 : m_oQuitProcess = true;
335 : }
336 0 : else if ( nInputBufLen == 0 )
337 : {
338 : // If we read an EOF, this is a signal that we should die.
339 0 : m_oQuitProcess = true;
340 : }
341 : else
342 : {
343 : // A message for the error.
344 0 : std::string szErrorMsg;
345 : // Now, is this a command which fits our requirements?
346 0 : m_ixpIndi.parseXml( ( char * )( &m_vecInputBuf[0] ), nInputBufLen, szErrorMsg );
347 :
348 0 : while( m_ixpIndi.getState() == IndiXmlParser::CompleteState )
349 : {
350 : // Create the message from the XML.
351 0 : IndiMessage imRecv = m_ixpIndi.createIndiMessage();
352 0 : const IndiProperty &ipRecv = imRecv.getProperty();
353 :
354 : // Dispatch!
355 0 : dispatch( imRecv.getType(), ipRecv );
356 :
357 : // Get ready for some new XML.
358 : //m_ixpIndi.clear();
359 :
360 : //Test whether there is more unparsed data.
361 0 : m_ixpIndi.parseXml( "", szErrorMsg);
362 0 : }
363 0 : }
364 : }
365 : }
366 0 : catch ( const runtime_error &excep )
367 : {
368 0 : }
369 0 : catch ( const exception &excep )
370 : {
371 0 : }
372 : }
373 :
374 3 : }
375 :
376 : ////////////////////////////////////////////////////////////////////////////////
377 : /// \brief IndiConnection::sendXml
378 : /// Sends an XML string out.
379 : /// \param szXml The XML to send.
380 6 : void IndiConnection::sendXml( const string &szXml ) const
381 : {
382 6 : MutexLock::AutoLock autoOut( &m_mutOutput );
383 :
384 6 : if(m_fdOutput < 0)
385 : {
386 0 : return;
387 : }
388 :
389 6 : const char * buf = szXml.c_str();
390 6 : size_t remaining = szXml.size();
391 :
392 12 : while(remaining > 0)
393 : {
394 6 : ssize_t nwr = ::write(m_fdOutput, buf, remaining);
395 6 : if(nwr > 0)
396 : {
397 6 : buf += nwr;
398 6 : remaining -= static_cast<size_t>(nwr);
399 6 : continue;
400 : }
401 :
402 0 : if(nwr < 0 && errno == EINTR)
403 : {
404 0 : continue;
405 : }
406 :
407 0 : break;
408 : }
409 :
410 6 : }
411 :
412 : ////////////////////////////////////////////////////////////////////////////////
413 : /// \brief IndiConnection::isVerboseModeEnabled
414 : /// Are we logging additional messages? True if yes, false otherwise.
415 : /// \return true or false.
416 :
417 0 : bool IndiConnection::isVerboseModeEnabled() const
418 : {
419 0 : return m_oIsVerboseModeEnabled;
420 : }
421 :
422 : ////////////////////////////////////////////////////////////////////////////////
423 : /// \brief IndiConnection::enableVerboseMode
424 : /// Turns the additional logging on or off.
425 : /// \param oEnable true or false to turn it on or off.
426 :
427 0 : void IndiConnection::enableVerboseMode( const bool &oEnable )
428 : {
429 0 : m_oIsVerboseModeEnabled = oEnable;
430 0 : }
431 :
432 : ////////////////////////////////////////////////////////////////////////////////
433 : /// \brief setInputFd
434 : /// Which FD will be used for input?
435 : /// \param iFd The file descriptor to use.
436 :
437 3 : void IndiConnection::setInputFd( const int &iFd )
438 : {
439 3 : m_fdInput = iFd;
440 3 : }
441 :
442 : ////////////////////////////////////////////////////////////////////////////////
443 : /// \brief setOutputFd
444 : /// Which FD will be used for output?
445 : /// \param iFd The file descriptor to use.
446 :
447 3 : void IndiConnection::setOutputFd( const int &iFd )
448 : {
449 3 : MutexLock::AutoLock autoOut( &m_mutOutput );
450 :
451 3 : if(iFd == m_fdOutput)
452 : {
453 0 : return;
454 : }
455 :
456 3 : if(m_fdOutput >= 0 && m_fdOutput != STDOUT_FILENO)
457 : {
458 0 : ::close(m_fdOutput);
459 : }
460 :
461 3 : m_fdOutput = iFd;
462 3 : }
463 :
464 : ////////////////////////////////////////////////////////////////////////////////
465 : /// \brief IndiConnection::setName
466 : /// Sets the name of this component.
467 : /// \param szName The name to use.
468 :
469 12 : void IndiConnection::setName( const string &szName )
470 : {
471 12 : m_szName = szName;
472 12 : }
473 :
474 : ////////////////////////////////////////////////////////////////////////////////
475 : /// \brief IndiConnection::getName
476 : /// Returns the name of this component.
477 : /// \return The name
478 :
479 12 : string IndiConnection::getName() const
480 : {
481 12 : return m_szName;
482 : }
483 :
484 : ////////////////////////////////////////////////////////////////////////////////
485 : /// \brief IndiConnection::setVersion
486 : /// Sets the version of this component.
487 : /// \param szVersion The version as a string.
488 :
489 12 : void IndiConnection::setVersion( const string &szVersion )
490 : {
491 12 : m_szVersion = szVersion;
492 12 : }
493 :
494 : ////////////////////////////////////////////////////////////////////////////////
495 : /// \brief IndiConnection::getVersion
496 : /// Returns the version of this component.
497 : /// \return The version as a string.
498 :
499 12 : string IndiConnection::getVersion() const
500 : {
501 12 : return m_szVersion;
502 : }
503 :
504 : ////////////////////////////////////////////////////////////////////////////////
505 : /// \brief IndiConnection::setProtocolVersion
506 : /// Sets the INDI protocol version.
507 : /// \param ProtocolVersion The protocol version as a string.
508 :
509 12 : void IndiConnection::setProtocolVersion( const string &ProtocolVersion )
510 : {
511 12 : m_ixpIndi.setProtocolVersion( ProtocolVersion );
512 12 : }
513 :
514 : ////////////////////////////////////////////////////////////////////////////////
515 : /// \brief IndiConnection::getProtocolVersion
516 : /// Returns the INDI protocol version.
517 : /// \param szVersion The protocol version as a string.
518 :
519 18 : string IndiConnection::getProtocolVersion() const
520 : {
521 18 : return m_ixpIndi.getProtocolVersion();
522 : }
523 :
524 : ////////////////////////////////////////////////////////////////////////////////
525 : /// \brief IndiConnection::quitProcess
526 : /// Causes the process to quit, the same as if a ctrl-c was sent.
527 :
528 3 : void IndiConnection::quitProcess()
529 : {
530 3 : m_oQuitProcess = true;
531 3 : }
532 :
533 : ////////////////////////////////////////////////////////////////////////////////
|