]> git.mxchange.org Git - flightgear.git/blob - src/Network/props.cxx
cosmetic changes *only*:
[flightgear.git] / src / Network / props.cxx
1 // \file props.cxx
2 // Property server class.
3 //
4 // Written by Curtis Olson, started September 2000.
5 // Modified by Bernie Bright, May 2002.
6 //
7 // Copyright (C) 2000  Curtis L. Olson - http://www.flightgear.org/~curt
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License as
11 // published by the Free Software Foundation; either version 2 of the
12 // License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful, but
15 // WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 // General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with this program; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
22 //
23 // $Id$
24
25
26 #ifdef HAVE_CONFIG_H
27 #  include <config.h>
28 #endif
29
30 #include <simgear/compiler.h>
31 #include <simgear/debug/logstream.hxx>
32 #include <simgear/structure/commands.hxx>
33 #include <simgear/misc/strutils.hxx>
34 #include <simgear/props/props.hxx>
35 #include <simgear/props/props_io.hxx>
36
37 #include <sstream>
38 #include <iostream>
39
40 #include <Main/globals.hxx>
41 #include <Main/viewmgr.hxx>
42
43 #include <plib/netChat.h>
44
45 #include "props.hxx"
46
47 SG_USING_STD(stringstream);
48 SG_USING_STD(ends);
49
50 using std::cout;
51 using std::endl;
52
53 /**
54  * Props connection class.
55  * This class represents a connection to props client.
56  */
57 class PropsChannel : public netChat
58 {
59     netBuffer buffer;
60
61     /**
62      * Current property node name.
63      */
64     string path;
65
66     enum Mode {
67         PROMPT,
68         DATA
69     };
70     Mode mode;
71
72 public:
73     /**
74      * Constructor.
75      */
76     PropsChannel();
77
78     /**
79      * Append incoming data to our request buffer.
80      *
81      * @param s Character string to append to buffer
82      * @param n Number of characters to append.
83      */
84     void collectIncomingData( const char* s, int n );
85
86     /**
87      * Process a complete request from the props client.
88      */
89     void foundTerminator();
90
91 private:
92     inline void node_not_found_error( const string& s ) const {
93         throw "node '" + s + "' not found";
94     }
95 };
96
97 /**
98  *
99  */
100 PropsChannel::PropsChannel()
101     : buffer(512),
102       path("/"),
103       mode(PROMPT)
104 {
105     setTerminator( "\r\n" );
106 }
107
108 /**
109  *
110  */
111 void
112 PropsChannel::collectIncomingData( const char* s, int n )
113 {
114     buffer.append( s, n );
115 }
116
117 // return a human readable form of the value "type"
118 static string
119 getValueTypeString( const SGPropertyNode *node )
120 {
121     string result;
122
123     if ( node == NULL )
124     {
125         return "unknown";
126     }
127
128     SGPropertyNode::Type type = node->getType();
129     if ( type == SGPropertyNode::UNSPECIFIED ) {
130         result = "unspecified";
131     } else if ( type == SGPropertyNode::NONE ) {
132         result = "none";
133     } else if ( type == SGPropertyNode::BOOL ) {
134         result = "bool";
135     } else if ( type == SGPropertyNode::INT ) {
136         result = "int";
137     } else if ( type == SGPropertyNode::LONG ) {
138         result = "long";
139     } else if ( type == SGPropertyNode::FLOAT ) {
140         result = "float";
141     } else if ( type == SGPropertyNode::DOUBLE ) {
142         result = "double";
143     } else if ( type == SGPropertyNode::STRING ) {
144         result = "string";
145     }
146
147     return result;
148 }
149
150 /**
151  * We have a command.
152  *
153  */
154 void
155 PropsChannel::foundTerminator()
156 {
157     const char* cmd = buffer.getData();
158     SG_LOG( SG_IO, SG_INFO, "processing command = \"" << cmd << "\"" );
159
160     vector<string> tokens = simgear::strutils::split( cmd );
161
162     SGPropertyNode* node = globals->get_props()->getNode( path.c_str() );
163
164     try {
165         if (!tokens.empty()) {
166             string command = tokens[0];
167
168             if (command == "ls") {
169                 SGPropertyNode* dir = node;
170                 if (tokens.size() == 2) {
171                     if (tokens[1][0] == '/') {
172                         dir = globals->get_props()->getNode( tokens[1].c_str() );
173                     } else {
174                         string s = path;
175                         s += "/";
176                         s += tokens[1];
177                         dir = globals->get_props()->getNode( s.c_str() );
178                     }
179
180                     if (dir == 0) {
181                         node_not_found_error( tokens[1] );
182                     }
183                 }
184
185                 for (int i = 0; i < dir->nChildren(); i++) {
186                     SGPropertyNode * child = dir->getChild(i);
187                     string line = child->getDisplayName(true);
188
189                     if ( child->nChildren() > 0 ) {
190                         line += "/";
191                     } else {
192                         if (mode == PROMPT) {
193                             string value = child->getStringValue();
194                             line += " =\t'" + value + "'\t(";
195                             line += getValueTypeString( child );
196                             line += ")";
197                         }
198                     }
199
200                     line += getTerminator();
201                     push( line.c_str() );
202                 }
203             } else if ( command == "dump" ) {
204                 stringstream buf;
205                 if ( tokens.size() <= 1 ) {
206                     writeProperties( buf, node );
207                     buf << ends; // null terminate the string
208                     push( buf.str().c_str() );
209                     push( getTerminator() );
210                 } else {
211                     SGPropertyNode *child = node->getNode( tokens[1].c_str() );
212                     if ( child ) {
213                         writeProperties ( buf, child );
214                         buf << ends; // null terminate the string
215                         push( buf.str().c_str() );
216                         push( getTerminator() );
217                     } else {
218                         node_not_found_error( tokens[1] );
219                     }
220                 }
221             }
222             else if ( command == "cd" ) {
223                 if (tokens.size() == 2) {
224                     SGPropertyNode* child = node->getNode( tokens[1].c_str() );
225                     if ( child ) {
226                         node = child;
227                         path = node->getPath();
228                     } else {
229                         node_not_found_error( tokens[1] );
230                     }
231                 }
232             } else if ( command == "pwd" ) {
233                 string pwd = node->getPath();
234                 if (pwd.empty()) {
235                     pwd = "/";
236                 }
237
238                 push( pwd.c_str() );
239                 push( getTerminator() );
240             } else if ( command == "get" || command == "show" ) {
241                 if ( tokens.size() == 2 ) {
242                     string tmp;
243                     string value = node->getStringValue ( tokens[1].c_str(), "" );
244                     if ( mode == PROMPT ) {
245                         tmp = tokens[1];
246                         tmp += " = '";
247                         tmp += value;
248                         tmp += "' (";
249                         tmp += getValueTypeString(
250                                          node->getNode( tokens[1].c_str() ) );
251                         tmp += ")";
252                     } else {
253                         tmp = value;
254                     }
255                     push( tmp.c_str() );
256                     push( getTerminator() );
257                 }
258             } else if ( command == "set" ) {
259                 if ( tokens.size() >= 2 ) {
260                     string value, tmp;
261                     for (unsigned int i = 2; i < tokens.size(); i++) {
262                         if (i > 2)
263                             value += " ";
264                         value += tokens[i];
265                     }
266                     node->getNode( tokens[1].c_str(), true )
267                         ->setStringValue(value.c_str());
268
269                     if ( mode == PROMPT ) {
270                         // now fetch and write out the new value as confirmation
271                         // of the change
272                         value = node->getStringValue ( tokens[1].c_str(), "" );
273                         tmp = tokens[1] + " = '" + value + "' (";
274                         tmp += getValueTypeString( node->getNode( tokens[1].c_str() ) );
275                         tmp += ")";
276                         push( tmp.c_str() );
277                         push( getTerminator() );
278                     }
279                 }
280             } else if ( command == "reinit" ) {
281                 if ( tokens.size() == 2 ) {
282                     string tmp;
283                     SGPropertyNode args;
284                     for ( unsigned int i = 1; i < tokens.size(); ++i ) {
285                         cout << "props: adding subsystem = " << tokens[i] << endl;
286                         SGPropertyNode *node = args.getNode("subsystem", i-1, true);
287                         node->setStringValue( tokens[i].c_str() );
288                     }
289                     if ( !globals->get_commands()
290                              ->execute( "reinit", &args) )
291                     {
292                         SG_LOG( SG_GENERAL, SG_ALERT,
293                                 "Command " << tokens[1] << " failed.");
294                         if ( mode == PROMPT ) {
295                             tmp += "*failed*";
296                             push( tmp.c_str() );
297                             push( getTerminator() );
298                         }
299                     } else {
300                         if ( mode == PROMPT ) {
301                             tmp += "<completed>";
302                             push( tmp.c_str() );
303                             push( getTerminator() );
304                         }
305                     }
306                 }
307             } else if ( command == "run" ) {
308                 string tmp;
309                 if ( tokens.size() >= 2 ) {
310                     SGPropertyNode args;
311                     if ( tokens[1] == "reinit" ) {
312                         for ( unsigned int i = 2; i < tokens.size(); ++i ) {
313                             cout << "props: adding subsystem = " << tokens[i]
314                                  << endl;
315                             SGPropertyNode *node
316                                 = args.getNode("subsystem", i-2, true);
317                             node->setStringValue( tokens[i].c_str() );
318                         }
319                     } else if ( tokens[1] == "set-sea-level-air-temp-degc" ) {
320                         for ( unsigned int i = 2; i < tokens.size(); ++i ) {
321                             cout << "props: set-sl command = " << tokens[i]
322                                  << endl;
323                             SGPropertyNode *node
324                                 = args.getNode("temp-degc", i-2, true);
325                             node->setStringValue( tokens[i].c_str() );
326                         }
327                     } else if ( tokens[1] == "set-outside-air-temp-degc" ) {
328                         for ( unsigned int i = 2; i < tokens.size(); ++i ) {
329                             cout << "props: set-oat command = " << tokens[i]
330                                  << endl;
331                             SGPropertyNode *node
332                                 = args.getNode("temp-degc", i-2, true);
333                             node->setStringValue( tokens[i].c_str() );
334                         }
335                     } else if ( tokens[1] == "timeofday" ) {
336                         for ( unsigned int i = 2; i < tokens.size(); ++i ) {
337                             cout << "props: time of day command = " << tokens[i]
338                                  << endl;
339                             SGPropertyNode *node
340                                 = args.getNode("timeofday", i-2, true);
341                             node->setStringValue( tokens[i].c_str() );
342                         }
343                     } else if ( tokens[1] == "play-audio-message" ) {
344                         if ( tokens.size() == 4 ) {
345                             cout << "props: play audio message = " << tokens[2]
346                                  << " " << tokens[3] << endl;
347                             SGPropertyNode *node;
348                             node = args.getNode("path", 0, true);
349                             node->setStringValue( tokens[2].c_str() );
350                             node = args.getNode("file", 0, true);
351                             node->setStringValue( tokens[3].c_str() );
352                        }
353                     }
354                     if ( !globals->get_commands()
355                              ->execute(tokens[1].c_str(), &args) )
356                     {
357                         SG_LOG( SG_GENERAL, SG_ALERT,
358                                 "Command " << tokens[1] << " failed.");
359                         if ( mode == PROMPT ) {
360                             tmp += "*failed*";
361                             push( tmp.c_str() );
362                             push( getTerminator() );
363                         }
364                     } else {
365                         if ( mode == PROMPT ) {
366                             tmp += "<completed>";
367                             push( tmp.c_str() );
368                             push( getTerminator() );
369                         }
370                     }
371                 } else {
372                     if ( mode == PROMPT ) {
373                         tmp += "no command specified";
374                         push( tmp.c_str() );
375                         push( getTerminator() );
376                     }
377                 }
378             } else if ( command == "quit" || command == "exit" ) {
379                 close();
380                 shouldDelete();
381                 return;
382             } else if ( command == "data" ) {
383                 mode = DATA;
384             } else if ( command == "prompt" ) {
385                 mode = PROMPT;
386             } else {
387                 const char* msg = "\
388 Valid commands are:\r\n\
389 \r\n\
390 cd <dir>           cd to a directory, '..' to move back\r\n\
391 data               switch to raw data mode\r\n\
392 dump               dump current state (in xml)\r\n\
393 get <var>          show the value of a parameter\r\n\
394 help               show this help message\r\n\
395 ls [<dir>]         list directory\r\n\
396 prompt             switch to interactive mode (default)\r\n\
397 pwd                display your current path\r\n\
398 quit               terminate connection\r\n\
399 run <command>      run built in command\r\n\
400 set <var> <val>    set <var> to a new <val>\r\n";
401                 push( msg );
402             }
403         }
404
405     } catch ( const string& msg ) {
406         string error = "-ERR \"" + msg + "\"";
407         push( error.c_str() );
408         push( getTerminator() );
409     }
410
411     if ( mode == PROMPT ) {
412         string prompt = node->getPath();
413         if (prompt.empty()) {
414             prompt = "/";
415         }
416         prompt += "> ";
417         push( prompt.c_str() );
418     }
419
420     buffer.remove();
421 }
422
423 /**
424  *
425  */
426 FGProps::FGProps( const vector<string>& tokens )
427 {
428     // tokens:
429     //   props,port#
430     //   props,medium,direction,hz,hostname,port#,style
431     if (tokens.size() == 2) {
432         port = atoi( tokens[1].c_str() );
433         set_hz( 5 );                // default to processing requests @ 5Hz
434     } else if (tokens.size() == 7) {
435         char* endptr;
436         errno = 0;
437         int hz = strtol( tokens[3].c_str(), &endptr, 10 );
438         if (errno != 0) {
439            SG_LOG( SG_IO, SG_ALERT, "I/O poll frequency out of range" );
440            set_hz( 5 );           // default to processing requests @ 5Hz
441         } else {
442             SG_LOG( SG_IO, SG_INFO, "Setting I/O poll frequency to "
443                     << hz << " Hz");
444             set_hz( hz );
445         }
446         port = atoi( tokens[5].c_str() );
447     } else {
448         throw FGProtocolConfigError( "FGProps: incorrect number of configuration arguments" );
449     }
450 }
451
452 /**
453  *
454  */
455 FGProps::~FGProps()
456 {
457 }
458
459 /**
460  *
461  */
462 bool
463 FGProps::open()
464 {
465     if ( is_enabled() )
466     {
467         SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel "
468                 << "is already in use, ignoring" );
469         return false;
470     }
471
472     netChannel::open();
473     netChannel::bind( "", port );
474     netChannel::listen( 5 );
475     SG_LOG( SG_IO, SG_INFO, "Props server started on port " << port );
476
477     set_enabled( true );
478     return true;
479 }
480
481 /**
482  *
483  */
484 bool
485 FGProps::close()
486 {
487     SG_LOG( SG_IO, SG_INFO, "closing FGProps" );
488     return true;
489 }
490
491 /**
492  *
493  */
494 bool
495 FGProps::process()
496 {
497     netChannel::poll();
498     return true;
499 }
500
501 /**
502  *
503  */
504 void
505 FGProps::handleAccept()
506 {
507     netAddress addr;
508     int handle = accept( &addr );
509     SG_LOG( SG_IO, SG_INFO, "Props server accepted connection from "
510             << addr.getHost() << ":" << addr.getPort() );
511     PropsChannel* channel = new PropsChannel();
512     channel->setHandle( handle );
513 }