Subversion Repositories HomeAutomation

Rev

Rev 1959 | Rev 1987 | 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:
1959 runge 112
    net::SocketId client_id_;
1647 runge 113
    std::string prompt_;
1674 runge 114
    std::vector<unsigned char> buffer_;
1608 runge 115
 
1986 runge 116
    void SlotOnNewStateHandler(net::SocketId client_id, net::ClientState client_state)
1608 runge 117
    {
1647 runge 118
        if (client_state != net::CLIENT_STATE_CONNECTED)
1608 runge 119
        {
1664 runge 120
            std::cout << "Disconnected from server." << std::endl;
1647 runge 121
 
1608 runge 122
            this->client_id_ = 0;
1647 runge 123
            finish = true;
1664 runge 124
            on_message_condition.notify_all();
1608 runge 125
        }
126
    }
127
 
1986 runge 128
    void SlotOnNewClientHandler(net::SocketId id, net::SocketId server_id)
1608 runge 129
    {
1986 runge 130
    }
131
 
132
    void SlotOnNewDataHandler(net::SocketId client_id, common::Byteset data)
133
    {
1674 runge 134
        for (unsigned int n = 0; n < data.GetSize(); n++)
1608 runge 135
        {
1674 runge 136
            this->buffer_.push_back(data[n]);
137
        }
138
 
139
        while (this->buffer_.size() >= 8)
140
        {
141
            std::string command = "";
142
            command += (char)this->buffer_[0];
143
            command += (char)this->buffer_[1];
144
            command += (char)this->buffer_[2];
145
            command += (char)this->buffer_[3];
146
 
147
            std::string payload_length_str = "";
148
            payload_length_str += (char)this->buffer_[4];
149
            payload_length_str += (char)this->buffer_[5];
150
            payload_length_str += (char)this->buffer_[6];
151
            payload_length_str += (char)this->buffer_[7];
1609 runge 152
 
1674 runge 153
            unsigned int payload_length = boost::lexical_cast<unsigned int>(payload_length_str);
154
 
155
            if (this->buffer_.size() - 8 < payload_length)
156
            {
157
                return;
158
            }
159
 
160
            std::string payload = "";
161
            for (unsigned int n = 8; n < payload_length + 8; n++)
162
            {
163
                payload += (char)this->buffer_[n];
164
            }
165
 
166
            this->buffer_.erase(this->buffer_.begin(), this->buffer_.begin() + payload_length + 8);
167
 
1647 runge 168
            if (command == "TEXT")
1609 runge 169
            {
1674 runge 170
                std::cout << payload << std::flush;
1609 runge 171
            }
1647 runge 172
            else if (command == "PROM")
1608 runge 173
            {
1674 runge 174
                this->prompt_ = payload;
1647 runge 175
                on_message_condition.notify_all();
176
            }
177
            else if (command == "COMP")
178
            {
179
                autocomplete_list.clear();
1620 runge 180
 
1674 runge 181
                if (payload.length() > 0)
1620 runge 182
                {
1674 runge 183
                    boost::algorithm::split(autocomplete_list, payload, boost::is_any_of("\n"), boost::algorithm::token_compress_on);
1620 runge 184
                }
1647 runge 185
 
186
                on_message_condition.notify_all();
1608 runge 187
            }
1647 runge 188
            else
189
            {
1674 runge 190
                std::cerr << "Could not parse package." << std::endl;
1647 runge 191
                finish = true;
192
                on_message_condition.notify_all();
193
                break;
194
            }
1608 runge 195
        }
196
    }
197
};
198
 
199
ConsoleClient::Pointer cc;
200
 
1647 runge 201
 
1608 runge 202
int main(int argc, char **argv)
203
{
1647 runge 204
    // Signal handlers
1608 runge 205
    signal(SIGTERM, Handler);
206
    signal(SIGINT, Handler);
207
    signal(SIGQUIT, Handler);
208
    signal(SIGABRT, Handler);
209
    signal(SIGPIPE, Handler);
210
 
1647 runge 211
    boost::mutex guard_mutex;
212
 
213
    // Setup readline
1649 runge 214
    history_filename = GetUserHomeDirectory() + "/.atomic_history";
1647 runge 215
    read_history(history_filename.data());
216
 
217
    rl_attempted_completion_function = AutoComplete;
218
 
219
    // Save command line state
1608 runge 220
    tcgetattr(fileno(stdin), &original_flags);
221
 
1647 runge 222
    // Parse commandline
223
    boost::program_options::options_description command_line;
224
    boost::program_options::variables_map variable_map;
1608 runge 225
 
1647 runge 226
    command_line.add_options()
227
    ("help,h",    "produce help message")
1664 runge 228
    ("command,c", boost::program_options::value<std::string>()->default_value(""), "command")
1647 runge 229
    ("server,s",  boost::program_options::value<std::string>()->default_value("localhost"), "server address")
230
    ("port,p",    boost::program_options::value<unsigned int>()->default_value(1202), "server port");
1608 runge 231
 
232
    try
233
    {
1647 runge 234
        boost::program_options::store(boost::program_options::command_line_parser(argc, argv).options(command_line).run(), variable_map);
1608 runge 235
    }
1647 runge 236
    catch (boost::program_options::unknown_option e)
1608 runge 237
    {
1647 runge 238
        std::cerr << e.what() << std::endl;
239
        std::cout << command_line << std::endl;
1608 runge 240
        CleanUp();
241
        return EXIT_FAILURE;
242
    }
1647 runge 243
    catch (boost::program_options::invalid_syntax e)
244
    {
245
        std::cerr << e.what() << std::endl;
246
        std::cout << command_line << std::endl;
247
        CleanUp();
248
        return EXIT_FAILURE;
249
    }
1608 runge 250
 
1647 runge 251
    if (variable_map.count("help") != 0)
252
    {
253
        std::cout << command_line << std::endl;
254
        CleanUp();
255
        return EXIT_SUCCESS;
256
    }
1608 runge 257
 
1647 runge 258
    net::Manager::Create();
1664 runge 259
 
260
    if (variable_map["command"].as<std::string>() == "")
261
    {
262
        std::cout << "\033[29;1mAtom Interactive Console, version " + std::string(VERSION) + " starting...\033[0m" << std::endl;
1679 runge 263
        std::cout << "\033[29;1mReleased under " + std::string(LICENSE) + ".\033[0m" << std::endl;
1664 runge 264
        std::cout << "Written by Mattias Runge 2010." << std::endl;
265
 
266
        std::cout << "Connecting to " << variable_map["server"].as<std::string>().data() << ":" << variable_map["port"].as<unsigned int>() << "..." << std::endl;
267
    }
1609 runge 268
 
1647 runge 269
    try
270
    {
271
        cc = ConsoleClient::Pointer(new ConsoleClient(variable_map["server"].as<std::string>(), variable_map["port"].as<unsigned int>()));
272
    }
273
    catch (std::runtime_error& e)
274
    {
1664 runge 275
        std::cerr << "Connection error: " << e.what() << std::endl;
1647 runge 276
        CleanUp();
277
        return EXIT_FAILURE;
278
    }
1609 runge 279
 
1664 runge 280
    if (variable_map["command"].as<std::string>() != "")
1608 runge 281
    {
1647 runge 282
        boost::mutex::scoped_lock guard(guard_mutex);
283
        on_message_condition.wait(guard);
1608 runge 284
 
1664 runge 285
        if (!finish)
1608 runge 286
        {
1664 runge 287
            cc->SendResponse(variable_map["command"].as<std::string>());
288
 
289
            if (!finish)
290
            {
291
                on_message_condition.wait(guard);
292
            }
1608 runge 293
        }
1664 runge 294
    }
295
    else
296
    {
297
        while (true)
1620 runge 298
        {
1664 runge 299
            boost::mutex::scoped_lock guard(guard_mutex);
300
            on_message_condition.wait(guard);
301
 
302
            if (finish)
1647 runge 303
            {
1664 runge 304
                break;
1647 runge 305
            }
306
 
1664 runge 307
            while ((buffer = readline(cc->GetPrompt().data())) != NULL)
308
            {
1665 runge 309
                if (finish)
310
                {
311
                    break;
312
                }
313
 
1664 runge 314
                if (strlen(buffer) == 0)
315
                {
316
                    continue;
317
                }
318
 
319
                break;
320
            }
321
 
1665 runge 322
            if (finish)
323
            {
324
                break;
325
            }
326
 
1664 runge 327
            cc->SendResponse(buffer);
328
            add_history(buffer);
1620 runge 329
        }
1608 runge 330
    }
331
 
332
    CleanUp();
333
 
334
    return EXIT_SUCCESS;
335
}
336
 
337
static char** AutoComplete(const char* text, int start, int end)
338
{
339
    unsigned int count = 0;
1647 runge 340
    boost::mutex guard_mutex;
341
 
1914 linlun 342
    for (unsigned int n = 0; n < (unsigned int)start; n++)
1608 runge 343
    {
1609 runge 344
        if (rl_line_buffer[n] == ' ')
1608 runge 345
        {
346
            count++;
347
        }
348
    }
349
 
1647 runge 350
    cc->AutoCompleteRequest(count, rl_line_buffer);
1608 runge 351
 
352
    boost::mutex::scoped_lock guard(guard_mutex);
1647 runge 353
    on_message_condition.wait(guard);
1608 runge 354
 
1647 runge 355
    /*if (autocomplete_list.size() == 0)
1609 runge 356
    {
357
        return NULL;
1647 runge 358
    }*/
1609 runge 359
 
1608 runge 360
    return rl_completion_matches(text, &AutoCompleteGet);
361
}
362
 
363
char* AutoCompleteGet(const char* text, int state)
364
{
365
    static int index = 0;
366
    int length = strlen(text);
367
    std::string name;
368
 
369
    if (!state) // First run
370
    {
371
        index = 0;
372
    }
373
 
1914 linlun 374
    while (autocomplete_list.size() > (unsigned int) index)
1608 runge 375
    {
376
        name = autocomplete_list[index];
377
 
378
        index++;
379
 
380
        if (strncmp(name.data(), text, length) == 0)
381
        {
382
            char *result = (char*)malloc(name.size() + 1);
383
            strcpy(result, name.data());
384
 
385
            return result;
386
        }
387
    }
388
 
389
    return NULL;
390
}
391
 
392
void CleanUp()
393
{
1664 runge 394
    std::cout << std::endl;
395
    //std::cout << "Cleaning up..." << std::endl;
1609 runge 396
 
397
    write_history(history_filename.data());
1608 runge 398
 
399
    cc.reset();
400
 
401
    net::Manager::Delete();
402
 
403
    if (buffer != NULL)
404
    {
405
        free(buffer);
406
    }
407
 
1664 runge 408
    //std::cout << "Thank you for using Atom. Goodbye!" << std::endl;
1608 runge 409
 
410
    tcsetattr(fileno(stdin), TCSANOW, &original_flags); // Restore
411
}
412
 
413
void Handler(int status)
414
{
415
    std::string signal_name = "Unknown";
416
 
417
    switch (status)
418
    {
419
        case SIGTERM:
420
        {
421
            signal_name = "Terminate";
422
            break;
423
        }  
424
        case SIGINT:
425
        {
1940 runge 426
            signal_name = "Interrupt";
1608 runge 427
            break;
428
        }  
429
        case SIGQUIT:
430
        {
431
            signal_name = "Quit";
432
            break;
433
        }  
434
        case SIGABRT:
435
        {
436
            signal_name = "Abort";
437
            break;
438
        }  
439
        case SIGIO:
440
        {
441
            signal_name = "I/O";
442
            break;
443
        }  
444
        case SIGPIPE:
445
        {
446
            signal_name = "Pipe";
447
            break;
448
        }
449
    }
450
 
451
 
452
    if (status != SIGPIPE)
453
    {
454
        CleanUp();
455
        exit(0);
456
    }
1675 arune 457
}
458