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