Subversion Repositories HomeAutomation

Rev

Rev 1652 | Rev 1665 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | SVN | RSS feed

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