Subversion Repositories HomeAutomation

Rev

Rev 1647 | Rev 1652 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | SVN | RSS feed

Rev 1647 Rev 1649
1
/*
1
/*
2
 *
2
 *
3
 *  Copyright (C) 2010  Mattias Runge
3
 *  Copyright (C) 2010  Mattias Runge
4
 *
4
 *
5
 *  This program is free software; you can redistribute it and/or modify
5
 *  This program is free software; you can redistribute it and/or modify
6
 *  it under the terms of the GNU General Public License as published by
6
 *  it under the terms of the GNU General Public License as published by
7
 *  the Free Software Foundation; either version 2 of the License, or
7
 *  the Free Software Foundation; either version 2 of the License, or
8
 *  (at your option) any later version.
8
 *  (at your option) any later version.
9
 *
9
 *
10
 *  This program is distributed in the hope that it will be useful,
10
 *  This program is distributed in the hope that it will be useful,
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 *  GNU General Public License for more details.
13
 *  GNU General Public License for more details.
14
 *
14
 *
15
 *  You should have received a copy of the GNU General Public License along
15
 *  You should have received a copy of the GNU General Public License along
16
 *  with this program; if not, write to the Free Software Foundation, Inc.,
16
 *  with this program; if not, write to the Free Software Foundation, Inc.,
17
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
 *
18
 *
19
 */
19
 */
20
 
20
 
21
#include <string>
21
#include <string>
22
#include <vector>
22
#include <vector>
23
#include <iostream>
23
#include <iostream>
24
 
24
 
25
#include <signal.h>
25
#include <signal.h>
26
#include <stdio.h>
26
#include <stdio.h>
27
#include <stdlib.h>
27
#include <stdlib.h>
28
#include <pwd.h>
28
#include <pwd.h>
29
 
29
 
30
#include <boost/lexical_cast.hpp>
30
#include <boost/lexical_cast.hpp>
31
#include <boost/thread/mutex.hpp>
31
#include <boost/thread/mutex.hpp>
32
#include <boost/thread/condition.hpp>
32
#include <boost/thread/condition.hpp>
33
#include <boost/thread/locks.hpp>
33
#include <boost/thread/locks.hpp>
34
#include <boost/algorithm/string.hpp>
34
#include <boost/algorithm/string.hpp>
35
#include <boost/program_options.hpp>
35
#include <boost/program_options.hpp>
36
 
36
 
37
#include "net/Manager.h"
37
#include "net/Manager.h"
38
#include "net/Subscriber.h"
38
#include "net/Subscriber.h"
39
#include "net/types.h"
39
#include "net/types.h"
40
 
40
 
41
#include "common/common.h"
41
#include "common/common.h"
42
 
42
 
43
#include <readline/readline.h>
43
#include <readline/readline.h>
44
#include <readline/history.h>
44
#include <readline/history.h>
45
 
45
 
46
using namespace atom;
46
using namespace atom;
47
 
47
 
48
bool finish = false;
48
bool finish = false;
49
common::StringList autocomplete_list;
49
common::StringList autocomplete_list;
50
char* buffer = NULL;
50
char* buffer = NULL;
51
boost::condition on_message_condition;
51
boost::condition on_message_condition;
52
struct termios original_flags;
52
struct termios original_flags;
53
std::string history_filename;
53
std::string history_filename;
54
 
54
 
55
void Handler(int status);
55
void Handler(int status);
56
void CleanUp();
56
void CleanUp();
57
 
57
 
58
char* AutoCompleteGet(const char* text, int state);
58
char* AutoCompleteGet(const char* text, int state);
59
static char** AutoComplete(const char* text, int start, int end);
59
static char** AutoComplete(const char* text, int start, int end);
60
 
60
 
61
std::string GetCwd()
61
std::string GetUserHomeDirectory()
62
{
62
{
63
    return std::string(getpwuid(getuid())->pw_dir);
63
    return std::string(getpwuid(getuid())->pw_dir);
64
}
64
}
65
 
65
 
66
class ConsoleClient : public net::Subscriber
66
class ConsoleClient : public net::Subscriber
67
{
67
{
68
public:
68
public:
69
    typedef boost::shared_ptr<ConsoleClient> Pointer;
69
    typedef boost::shared_ptr<ConsoleClient> Pointer;
70
   
70
   
71
    ConsoleClient(std::string address, unsigned int port)
71
    ConsoleClient(std::string address, unsigned int port)
72
    {
72
    {
73
        this->client_id_ = net::Manager::Instance()->Connect(net::PROTOCOL_TCP, address, port);
73
        this->client_id_ = net::Manager::Instance()->Connect(net::PROTOCOL_TCP, address, port);
74
    }
74
    }
75
   
75
   
76
    virtual ~ConsoleClient()
76
    virtual ~ConsoleClient()
77
    {
77
    {
78
        net::Manager::Instance()->Disconnect(this->client_id_);
78
        net::Manager::Instance()->Disconnect(this->client_id_);
79
 
79
 
80
        this->io_service_.stop();
80
        this->io_service_.stop();
81
    }
81
    }
82
   
82
   
83
    std::string GetPrompt()
83
    std::string GetPrompt()
84
    {
84
    {
85
        return this->prompt_;
85
        return this->prompt_;
86
    }
86
    }
87
   
87
   
88
    void SendResponse(std::string payload)
88
    void SendResponse(std::string payload)
89
    {
89
    {
90
        std::string packet = "RESP";
90
        std::string packet = "RESP";
91
        packet += common::PadNumber(payload.length() + 1, 4);
91
        packet += common::PadNumber(payload.length() + 1, 4);
92
        packet += payload;
92
        packet += payload;
93
       
93
       
94
        net::Manager::Instance()->SendTo(this->client_id_, packet);
94
        net::Manager::Instance()->SendTo(this->client_id_, packet);
95
    }
95
    }
96
   
96
   
97
    void AutoCompleteRequest(unsigned int arg_index, std::string commandline)
97
    void AutoCompleteRequest(unsigned int arg_index, std::string commandline)
98
    {
98
    {
99
        std::string payload = common::PadNumber(arg_index, 4);
99
        std::string payload = common::PadNumber(arg_index, 4);
100
        payload += commandline;
100
        payload += commandline;
101
       
101
       
102
        std::string packet = "COMP";
102
        std::string packet = "COMP";
103
        packet += common::PadNumber(payload.length() + 1, 4);
103
        packet += common::PadNumber(payload.length() + 1, 4);
104
        packet += payload;
104
        packet += payload;
105
       
105
       
106
        net::Manager::Instance()->SendTo(this->client_id_, packet);
106
        net::Manager::Instance()->SendTo(this->client_id_, packet);
107
    }
107
    }
108
   
108
   
109
private:
109
private:
110
    net::ClientId client_id_;
110
    net::ClientId client_id_;
111
    std::string prompt_;
111
    std::string prompt_;
112
   
112
   
113
   
113
   
114
    void SlotOnNewStateHandler(net::ClientId client_id, net::ServerId server_id, net::ClientState client_state)
114
    void SlotOnNewStateHandler(net::ClientId client_id, net::ServerId server_id, net::ClientState client_state)
115
    {
115
    {
116
        if (client_state != net::CLIENT_STATE_CONNECTED)
116
        if (client_state != net::CLIENT_STATE_CONNECTED)
117
        {
117
        {
118
            std::cout << "Disconnected from server" << std::endl;
118
            std::cout << "Disconnected from server" << std::endl;
119
           
119
           
120
            this->client_id_ = 0;
120
            this->client_id_ = 0;
121
            finish = true;
121
            finish = true;
122
           
122
           
123
            kill(getpid(), SIGTERM);
123
            kill(getpid(), SIGTERM);
124
        }
124
        }
125
    }
125
    }
126
   
126
   
127
    void SlotOnNewDataHandler(net::ClientId client_id, net::ServerId server_id, common::Byteset data)
127
    void SlotOnNewDataHandler(net::ClientId client_id, net::ServerId server_id, common::Byteset data)
128
    {
128
    {
129
        std::string s(data.ToCharString());
129
        std::string s(data.ToCharString());
130
 
130
 
131
        while (s.length() > 0)
131
        while (s.length() > 0)
132
        {
132
        {
133
            std::string command = s.substr(0, 4);
133
            std::string command = s.substr(0, 4);
134
            unsigned int payload_length = boost::lexical_cast<unsigned int>(s.substr(4, 4));
134
            unsigned int payload_length = boost::lexical_cast<unsigned int>(s.substr(4, 4));
135
           
135
           
136
            if (command == "TEXT")
136
            if (command == "TEXT")
137
            {
137
            {
138
                std::cout << s.substr(8, payload_length - 1) << std::flush;
138
                std::cout << s.substr(8, payload_length - 1) << std::flush;
139
            }
139
            }
140
            else if (command == "PROM")
140
            else if (command == "PROM")
141
            {
141
            {
142
                this->prompt_ = s.substr(8, payload_length - 1);
142
                this->prompt_ = s.substr(8, payload_length - 1);
143
                on_message_condition.notify_all();
143
                on_message_condition.notify_all();
144
            }
144
            }
145
            else if (command == "COMP")
145
            else if (command == "COMP")
146
            {
146
            {
147
                autocomplete_list.clear();
147
                autocomplete_list.clear();
148
               
148
               
149
                if (payload_length > 0)
149
                if (payload_length > 0)
150
                {
150
                {
151
                    std::string payload = s.substr(8, payload_length - 1);
151
                    std::string payload = s.substr(8, payload_length - 1);
152
                   
152
                   
153
                    if (payload.length() > 0)
153
                    if (payload.length() > 0)
154
                    {
154
                    {
155
                        boost::algorithm::split(autocomplete_list, payload, boost::is_any_of("\n"), boost::algorithm::token_compress_on);
155
                        boost::algorithm::split(autocomplete_list, payload, boost::is_any_of("\n"), boost::algorithm::token_compress_on);
156
                    }
156
                    }
157
                }
157
                }
158
               
158
               
159
                on_message_condition.notify_all();
159
                on_message_condition.notify_all();
160
            }
160
            }
161
            else
161
            else
162
            {
162
            {
163
                std::cerr << "Unknown data received: " << s << std::endl;
163
                std::cerr << "Unknown data received: " << s << std::endl;
164
                finish = true;
164
                finish = true;
165
                on_message_condition.notify_all();
165
                on_message_condition.notify_all();
166
                break;
166
                break;
167
            }
167
            }
168
           
168
           
169
            if (8 + payload_length >= s.length())
169
            if (8 + payload_length >= s.length())
170
            {
170
            {
171
                break;
171
                break;
172
            }
172
            }
173
           
173
           
174
            s = s.substr(8 + payload_length);
174
            s = s.substr(8 + payload_length);
175
        }
175
        }
176
    }
176
    }
177
};
177
};
178
 
178
 
179
ConsoleClient::Pointer cc;
179
ConsoleClient::Pointer cc;
180
 
180
 
181
 
181
 
182
int main(int argc, char **argv)
182
int main(int argc, char **argv)
183
{
183
{
184
    // Signal handlers
184
    // Signal handlers
185
    signal(SIGTERM, Handler);
185
    signal(SIGTERM, Handler);
186
    signal(SIGINT, Handler);
186
    signal(SIGINT, Handler);
187
    signal(SIGQUIT, Handler);
187
    signal(SIGQUIT, Handler);
188
    signal(SIGABRT, Handler);
188
    signal(SIGABRT, Handler);
189
    signal(SIGPIPE, Handler);
189
    signal(SIGPIPE, Handler);
190
   
190
   
191
    boost::mutex guard_mutex;
191
    boost::mutex guard_mutex;
192
   
192
   
193
    // Setup readline
193
    // Setup readline
194
    history_filename = GetCwd() + "/.atomic_history";
194
    history_filename = GetUserHomeDirectory() + "/.atomic_history";
195
    read_history(history_filename.data());
195
    read_history(history_filename.data());
196
   
196
   
197
    rl_attempted_completion_function = AutoComplete;
197
    rl_attempted_completion_function = AutoComplete;
198
   
198
   
199
    // Save command line state
199
    // Save command line state
200
    tcgetattr(fileno(stdin), &original_flags);
200
    tcgetattr(fileno(stdin), &original_flags);
201
   
201
   
202
    // Parse commandline
202
    // Parse commandline
203
    boost::program_options::options_description command_line;
203
    boost::program_options::options_description command_line;
204
    boost::program_options::variables_map variable_map;
204
    boost::program_options::variables_map variable_map;
205
   
205
   
206
    command_line.add_options()
206
    command_line.add_options()
207
    ("help,h",    "produce help message")
207
    ("help,h",    "produce help message")
208
    ("server,s",  boost::program_options::value<std::string>()->default_value("localhost"), "server address")
208
    ("server,s",  boost::program_options::value<std::string>()->default_value("localhost"), "server address")
209
    ("port,p",    boost::program_options::value<unsigned int>()->default_value(1202), "server port");
209
    ("port,p",    boost::program_options::value<unsigned int>()->default_value(1202), "server port");
210
   
210
   
211
    try
211
    try
212
    {
212
    {
213
        boost::program_options::store(boost::program_options::command_line_parser(argc, argv).options(command_line).run(), variable_map);
213
        boost::program_options::store(boost::program_options::command_line_parser(argc, argv).options(command_line).run(), variable_map);
214
    }
214
    }
215
    catch (boost::program_options::unknown_option e)
215
    catch (boost::program_options::unknown_option e)
216
    {
216
    {
217
        std::cerr << e.what() << std::endl;
217
        std::cerr << e.what() << std::endl;
218
        std::cout << command_line << std::endl;
218
        std::cout << command_line << std::endl;
219
        CleanUp();
219
        CleanUp();
220
        return EXIT_FAILURE;
220
        return EXIT_FAILURE;
221
    }
221
    }
222
    catch (boost::program_options::invalid_syntax e)
222
    catch (boost::program_options::invalid_syntax e)
223
    {
223
    {
224
        std::cerr << e.what() << std::endl;
224
        std::cerr << e.what() << std::endl;
225
        std::cout << command_line << std::endl;
225
        std::cout << command_line << std::endl;
226
        CleanUp();
226
        CleanUp();
227
        return EXIT_FAILURE;
227
        return EXIT_FAILURE;
228
    }
228
    }
229
   
229
   
230
    if (variable_map.count("help") != 0)
230
    if (variable_map.count("help") != 0)
231
    {
231
    {
232
        std::cout << command_line << std::endl;
232
        std::cout << command_line << std::endl;
233
        CleanUp();
233
        CleanUp();
234
        return EXIT_SUCCESS;
234
        return EXIT_SUCCESS;
235
    }
235
    }
236
   
236
   
237
   
237
   
238
    std::cout << "Atom Interactive Console, version 1.5.0 starting..." << std::endl;
238
    std::cout << "Atom Interactive Console, version 1.5.0 starting..." << std::endl;
239
    std::cout << "Written by Mattias Runge 2010." << std::endl;
239
    std::cout << "Written by Mattias Runge 2010." << std::endl;
240
    std::cout << "Released under GPL version 2." << std::endl;
240
    std::cout << "Released under GPL version 2." << std::endl;
241
   
241
   
242
    net::Manager::Create();
242
    net::Manager::Create();
243
       
243
       
244
    std::cout << "Connecting to " << variable_map["server"].as<std::string>().data() << ":" << variable_map["port"].as<unsigned int>() << "..." << std::endl;
244
    std::cout << "Connecting to " << variable_map["server"].as<std::string>().data() << ":" << variable_map["port"].as<unsigned int>() << "..." << std::endl;
245
   
245
   
246
    try
246
    try
247
    {
247
    {
248
        cc = ConsoleClient::Pointer(new ConsoleClient(variable_map["server"].as<std::string>(), variable_map["port"].as<unsigned int>()));
248
        cc = ConsoleClient::Pointer(new ConsoleClient(variable_map["server"].as<std::string>(), variable_map["port"].as<unsigned int>()));
249
    }
249
    }
250
    catch (std::runtime_error& e)
250
    catch (std::runtime_error& e)
251
    {
251
    {
252
        std::cout << "error!" << std::endl;
252
        std::cout << "error!" << std::endl;
253
        std::cerr << e.what() << std::endl;
253
        std::cerr << e.what() << std::endl;
254
        CleanUp();
254
        CleanUp();
255
        return EXIT_FAILURE;
255
        return EXIT_FAILURE;
256
    }
256
    }
257
   
257
   
258
    while (true)
258
    while (true)
259
    {
259
    {
260
        boost::mutex::scoped_lock guard(guard_mutex);
260
        boost::mutex::scoped_lock guard(guard_mutex);
261
        on_message_condition.wait(guard);
261
        on_message_condition.wait(guard);
262
       
262
       
263
        if (finish)
263
        if (finish)
264
        {
264
        {
265
            break;
265
            break;
266
        }
266
        }
267
       
267
       
268
        while ((buffer = readline(cc->GetPrompt().data())) != NULL)
268
        while ((buffer = readline(cc->GetPrompt().data())) != NULL)
269
        {
269
        {
270
            if (strlen(buffer) == 0)
270
            if (strlen(buffer) == 0)
271
            {
271
            {
272
                continue;
272
                continue;
273
            }
273
            }
274
           
274
           
275
            break;
275
            break;
276
        }
276
        }
277
       
277
       
278
        cc->SendResponse(buffer);
278
        cc->SendResponse(buffer);
279
        add_history(buffer);
279
        add_history(buffer);
280
    }
280
    }
281
   
281
   
282
    CleanUp();
282
    CleanUp();
283
   
283
   
284
    return EXIT_SUCCESS;
284
    return EXIT_SUCCESS;
285
}
285
}
286
 
286
 
287
static char** AutoComplete(const char* text, int start, int end)
287
static char** AutoComplete(const char* text, int start, int end)
288
{
288
{
289
    unsigned int count = 0;
289
    unsigned int count = 0;
290
    boost::mutex guard_mutex;
290
    boost::mutex guard_mutex;
291
 
291
 
292
    for (unsigned int n = 0; n < start; n++)
292
    for (unsigned int n = 0; n < start; n++)
293
    {
293
    {
294
        if (rl_line_buffer[n] == ' ')
294
        if (rl_line_buffer[n] == ' ')
295
        {
295
        {
296
            count++;
296
            count++;
297
        }
297
        }
298
    }
298
    }
299
   
299
   
300
    cc->AutoCompleteRequest(count, rl_line_buffer);
300
    cc->AutoCompleteRequest(count, rl_line_buffer);
301
   
301
   
302
    boost::mutex::scoped_lock guard(guard_mutex);
302
    boost::mutex::scoped_lock guard(guard_mutex);
303
    on_message_condition.wait(guard);
303
    on_message_condition.wait(guard);
304
   
304
   
305
    /*if (autocomplete_list.size() == 0)
305
    /*if (autocomplete_list.size() == 0)
306
    {
306
    {
307
        return NULL;
307
        return NULL;
308
    }*/
308
    }*/
309
   
309
   
310
    return rl_completion_matches(text, &AutoCompleteGet);
310
    return rl_completion_matches(text, &AutoCompleteGet);
311
}
311
}
312
 
312
 
313
char* AutoCompleteGet(const char* text, int state)
313
char* AutoCompleteGet(const char* text, int state)
314
{
314
{
315
    static int index = 0;
315
    static int index = 0;
316
    int length = strlen(text);
316
    int length = strlen(text);
317
    std::string name;
317
    std::string name;
318
   
318
   
319
    if (!state) // First run
319
    if (!state) // First run
320
    {
320
    {
321
        index = 0;
321
        index = 0;
322
    }
322
    }
323
   
323
   
324
    while (autocomplete_list.size() > index)
324
    while (autocomplete_list.size() > index)
325
    {
325
    {
326
        name = autocomplete_list[index];
326
        name = autocomplete_list[index];
327
       
327
       
328
        index++;
328
        index++;
329
       
329
       
330
        if (strncmp(name.data(), text, length) == 0)
330
        if (strncmp(name.data(), text, length) == 0)
331
        {
331
        {
332
            char *result = (char*)malloc(name.size() + 1);
332
            char *result = (char*)malloc(name.size() + 1);
333
            strcpy(result, name.data());
333
            strcpy(result, name.data());
334
           
334
           
335
            return result;
335
            return result;
336
        }
336
        }
337
    }
337
    }
338
   
338
   
339
    return NULL;
339
    return NULL;
340
}
340
}
341
 
341
 
342
void CleanUp()
342
void CleanUp()
343
{
343
{
344
    std::cout << "Cleaning up..." << std::endl;
344
    std::cout << "Cleaning up..." << std::endl;
345
 
345
 
346
    write_history(history_filename.data());
346
    write_history(history_filename.data());
347
   
347
   
348
    cc.reset();
348
    cc.reset();
349
   
349
   
350
    net::Manager::Delete();
350
    net::Manager::Delete();
351
   
351
   
352
    if (buffer != NULL)
352
    if (buffer != NULL)
353
    {
353
    {
354
        free(buffer);
354
        free(buffer);
355
    }
355
    }
356
   
356
   
357
    std::cout << "Thank you for using Atom. Goodbye!" << std::endl;
357
    std::cout << "Thank you for using Atom. Goodbye!" << std::endl;
358
   
358
   
359
    tcsetattr(fileno(stdin), TCSANOW, &original_flags); // Restore
359
    tcsetattr(fileno(stdin), TCSANOW, &original_flags); // Restore
360
}
360
}
361
 
361
 
362
void Handler(int status)
362
void Handler(int status)
363
{
363
{
364
    std::string signal_name = "Unknown";
364
    std::string signal_name = "Unknown";
365
   
365
   
366
    switch (status)
366
    switch (status)
367
    {
367
    {
368
        case SIGTERM:
368
        case SIGTERM:
369
        {
369
        {
370
            signal_name = "Terminate";
370
            signal_name = "Terminate";
371
            break;
371
            break;
372
        }  
372
        }  
373
        case SIGINT:
373
        case SIGINT:
374
        {
374
        {
375
            signal_name = "Interupt";
375
            signal_name = "Interupt";
376
            break;
376
            break;
377
        }  
377
        }  
378
        case SIGQUIT:
378
        case SIGQUIT:
379
        {
379
        {
380
            signal_name = "Quit";
380
            signal_name = "Quit";
381
            break;
381
            break;
382
        }  
382
        }  
383
        case SIGABRT:
383
        case SIGABRT:
384
        {
384
        {
385
            signal_name = "Abort";
385
            signal_name = "Abort";
386
            break;
386
            break;
387
        }  
387
        }  
388
        case SIGIO:
388
        case SIGIO:
389
        {
389
        {
390
            signal_name = "I/O";
390
            signal_name = "I/O";
391
            break;
391
            break;
392
        }  
392
        }  
393
        case SIGPIPE:
393
        case SIGPIPE:
394
        {
394
        {
395
            signal_name = "Pipe";
395
            signal_name = "Pipe";
396
            break;
396
            break;
397
        }
397
        }
398
    }
398
    }
399
   
399
   
400
   
400
   
401
    if (status != SIGPIPE)
401
    if (status != SIGPIPE)
402
    {
402
    {
403
        CleanUp();
403
        CleanUp();
404
        exit(0);
404
        exit(0);
405
    }
405
    }
406
}
406
}