]> git.mxchange.org Git - flightgear.git/blob - src/Network/props.cxx
8752da41b927eb48ec5d9c2cd96ec6813d22443f
[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 - curt@flightgear.org
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., 675 Mass Ave, Cambridge, MA 02139, 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/misc/strutils.hxx>
33 #include <simgear/misc/props.hxx>
34 #include <simgear/misc/props_io.hxx>
35
36 #include STL_STRSTREAM
37
38 #include <Main/globals.hxx>
39 #include <Main/viewmgr.hxx>
40
41 #include <plib/netChat.h>
42
43 #include "props.hxx"
44
45 #if !defined(SG_HAVE_NATIVE_SGI_COMPILERS)
46 SG_USING_STD(strstream);
47 SG_USING_STD(ends);
48 #endif
49
50 /**
51  * Props connection class.
52  * This class represents a connection to props client.
53  */
54 class PropsChannel : public netChat
55 {
56     netBuffer buffer;
57
58     /**
59      * Current property node name.
60      */
61     string path;
62
63     enum Mode {
64         PROMPT,
65         DATA
66     };
67     Mode mode;
68
69 public:
70     /**
71      * Constructor.
72      */
73     PropsChannel();
74     
75     /**
76      * Append incoming data to our request buffer.
77      *
78      * @param s Character string to append to buffer
79      * @param n Number of characters to append.
80      */
81     void collectIncomingData( const char* s, int n );
82
83     /**
84      * Process a complete request from the props client.
85      */
86     void foundTerminator();
87
88 private:
89     /**
90      * Return a "Node no found" error message to the client.
91      */
92     void node_not_found_error( const string& node_name );
93 };
94
95 /**
96  * 
97  */
98 PropsChannel::PropsChannel()
99     : buffer(512),
100       path("/"),
101       mode(PROMPT)
102 {
103     setTerminator( "\r\n" );
104 }
105
106 /**
107  * 
108  */
109 void
110 PropsChannel::collectIncomingData( const char* s, int n )
111 {
112     buffer.append( s, n );
113 }
114
115 /**
116  * 
117  */
118 void
119 PropsChannel::node_not_found_error( const string& node_name )
120 {
121     string error = "ERR Node \"";
122     error += node_name;
123     error += "\" not found.";
124     push( error.c_str() );
125     push( getTerminator() );
126 }
127
128 // return a human readable form of the value "type"
129 static string
130 getValueTypeString( const SGPropertyNode *node )
131 {
132     string result;
133
134     if ( node == NULL )
135     {
136         return "unknown";
137     }
138
139     SGPropertyNode::Type type = node->getType();
140     if ( type == SGPropertyNode::UNSPECIFIED ) {
141         result = "unspecified";
142     } else if ( type == SGPropertyNode::NONE ) {
143         result = "none";
144     } else if ( type == SGPropertyNode::BOOL ) {
145         result = "bool";
146     } else if ( type == SGPropertyNode::INT ) {
147         result = "int";
148     } else if ( type == SGPropertyNode::LONG ) {
149         result = "long";
150     } else if ( type == SGPropertyNode::FLOAT ) {
151         result = "float";
152     } else if ( type == SGPropertyNode::DOUBLE ) {
153         result = "double";
154     } else if ( type == SGPropertyNode::STRING ) {
155         result = "string";
156     }
157
158     return result;
159 }
160
161 /**
162  * We have a command.
163  * 
164  */
165 void
166 PropsChannel::foundTerminator()
167 {
168     const char* cmd = buffer.getData();
169     SG_LOG( SG_IO, SG_INFO, "processing command = \"" << cmd << "\"" );
170
171     vector<string> tokens = simgear::strutils::split( cmd );
172
173     SGPropertyNode* node = globals->get_props()->getNode( path.c_str() );
174
175     if (!tokens.empty())
176     {
177         string command = tokens[0];
178
179         if (command == "ls")
180         {
181             SGPropertyNode* dir = node;
182             if (tokens.size() == 2)
183             {
184                 if (tokens[1][0] == '/')
185                 {
186                     dir = globals->get_props()->getNode( tokens[1].c_str() );
187                 }
188                 else
189                 {
190                     string s = path;
191                     s += "/";
192                     s += tokens[1];
193                     dir = globals->get_props()->getNode( s.c_str() );
194                 }
195
196                 if (dir == 0)
197                 {
198                     node_not_found_error( tokens[1] );
199                     goto prompt;
200                 }
201             }
202
203             for (int i = 0; i < dir->nChildren(); i++)
204             {
205                 SGPropertyNode * child = dir->getChild(i);
206                 string name = child->getName();
207                 string line = name;
208
209                 if (dir->getChild( name.c_str(), 1 ))
210                 {
211                     char buf[16];
212                     sprintf(buf, "[%d]", child->getIndex());
213                     line += buf;
214                 }
215
216                 if ( child->nChildren() > 0 )
217                 {
218                     line += "/";
219                 }
220                 else
221                 {
222                     if (mode == PROMPT)
223                     {
224                         string value = dir->getStringValue( name.c_str(), "" );
225                         line += " =\t'" + value + "'\t(";
226                         line += getValueTypeString(
227                                         dir->getNode( name.c_str() ) );
228                         line += ")";
229                     }
230                 }
231
232                 line += getTerminator();
233                 push( line.c_str() );
234             }
235         }
236         else if ( command == "dump" )
237         {
238             strstream buf;
239             if ( tokens.size() <= 1 )
240             {
241                 writeProperties( buf, node );
242                 buf << ends; // null terminate the string
243                 push( buf.str() );
244                 push( getTerminator() );
245             }
246             else
247             {
248                 SGPropertyNode *child = node->getNode( tokens[1].c_str() );
249                 if ( child )
250                 {
251                     writeProperties ( buf, child );
252                     buf << ends; // null terminate the string
253                     push( buf.str() );
254                     push( getTerminator() );
255                 }
256                 else
257                 {
258                     node_not_found_error( tokens[1] );
259                 }
260             }
261         }
262         else if ( command == "cd" )
263         {
264             if (tokens.size() == 2)
265             {
266                 try
267                 {
268                     SGPropertyNode* child = node->getNode( tokens[1].c_str() );
269                     if ( child )
270                     {
271                         node = child;
272                         path = node->getPath();
273                     }
274                     else
275                     {
276                         node_not_found_error( tokens[1] );
277                     }
278                 }
279                 catch (...)
280                 {
281                     // Ignore attempt to move past root node with ".."
282                 }
283             }
284         }
285         else if ( command == "pwd" )
286         {
287             string ttt = node->getPath();
288             if (ttt.empty())
289             {
290                 ttt = "/";
291             }
292
293             push( ttt.c_str() );
294             push( getTerminator() );
295         }
296         else if ( command == "get" || command == "show" )
297         {
298             if ( tokens.size() == 2 )
299             {
300                 string tmp;     
301                 string value = node->getStringValue ( tokens[1].c_str(), "" );
302                 if ( mode == PROMPT )
303                 {
304                     tmp = tokens[1];
305                     tmp += " = '";
306                     tmp += value;
307                     tmp += "' (";
308                     tmp += getValueTypeString(
309                                      node->getNode( tokens[1].c_str() ) );
310                     tmp += ")";
311                 }
312                 else
313                 {
314                     tmp = value;
315                 }
316                 push( tmp.c_str() );
317                 push( getTerminator() );
318             }
319         }
320         else if ( command == "set" )
321         {
322             if ( tokens.size() == 3 )
323             {
324                 node->getNode( tokens[1].c_str(), true )->setStringValue(tokens[2].c_str());
325
326                 if ( mode == PROMPT )
327                 {
328                     // now fetch and write out the new value as confirmation
329                     // of the change
330                     string value = node->getStringValue ( tokens[1].c_str(), "" );
331                     string tmp = tokens[1] + " = '" + value + "' (";
332                     tmp += getValueTypeString( node->getNode( tokens[1].c_str() ) );
333                     tmp += ")";
334                     push( tmp.c_str() );
335                     push( getTerminator() );
336                 }
337             }
338         }
339         else if (command == "quit")
340         {
341             close();
342             shouldDelete();
343             return;
344         }
345         else if ( command == "data" )
346         {
347             mode = DATA;
348         }
349         else if ( command == "prompt" )
350         {
351             mode = PROMPT;
352         }
353         else
354         {
355             const char* msg = "\
356 Valid commands are:\r\n\
357 \r\n\
358 cd <dir>           cd to a directory, '..' to move back\r\n\
359 data               switch to raw data mode\r\n\
360 dump               dump current state (in xml)\r\n\
361 get <var>          show the value of a parameter\r\n\
362 help               show this help message\r\n\
363 ls [<dir>]         list directory\r\n\
364 prompt             switch to interactive mode (default)\r\n\
365 pwd                display your current path\r\n\
366 quit               terminate connection\r\n\
367 set <var> <val>    set <var> to a new <val>\r\n\
368 show <var>         synonym for get\r\n";
369             push( msg );
370         }
371     }
372
373  prompt:
374     if (mode == PROMPT)
375     {
376         string prompt = node->getPath();
377         if (prompt.empty())
378         {
379             prompt = "/";
380         }
381         prompt += "> ";
382         push( prompt.c_str() );
383     }
384
385     buffer.remove();
386 }
387
388 /**
389  * 
390  */
391 FGProps::FGProps( const vector<string>& tokens )
392 {
393     // tokens:
394     //   props,port#
395     //   props,medium,direction,hz,hostname,port#,style
396     if (tokens.size() == 2)
397         port = atoi( tokens[1].c_str() );
398     else if (tokens.size() == 7)
399         port = atoi( tokens[5].c_str() );
400     else
401         throw FGProtocolConfigError( "FGProps: incorrect number of configuration arguments" );
402 }
403
404 /**
405  * 
406  */
407 FGProps::~FGProps()
408 {
409 }
410
411 /**
412  * 
413  */
414 bool
415 FGProps::open()
416 {
417     if ( is_enabled() )
418     {
419         SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel " 
420                 << "is already in use, ignoring" );
421         return false;
422     }
423
424     netChannel::open();
425     netChannel::bind( "", port );
426     netChannel::listen( 5 );
427     SG_LOG( SG_IO, SG_INFO, "Props server started on port " << port );
428
429     set_hz( 5 );                // default to processing requests @ 5Hz
430     set_enabled( true );
431     return true;
432 }
433
434 /**
435  * 
436  */
437 bool
438 FGProps::close()
439 {
440     return true;
441 }
442
443 /**
444  * 
445  */
446 bool
447 FGProps::process()
448 {
449     netChannel::poll();
450     return true;
451 }
452
453 /**
454  * 
455  */
456 void
457 FGProps::handleAccept()
458 {
459     netAddress addr;
460     int handle = accept( &addr );
461     SG_LOG( SG_IO, SG_INFO, "Props server accepted connection from "
462             << addr.getHost() << ":" << addr.getPort() );
463     PropsChannel* channel = new PropsChannel();
464     channel->setHandle( handle );
465 }