Switch time() calls to monotonic clock calls (#7597)
[libt2n] / src / command_server.cpp
1 /*
2 Copyright (C) 2006 by Intra2net AG - Gerd v. Egidy
3
4 The software in this package is distributed under the GNU General
5 Public License version 2 (with a special exception described below).
6
7 A copy of GNU General Public License (GPL) is included in this distribution,
8 in the file COPYING.GPL.
9
10 As a special exception, if other files instantiate templates or use macros
11 or inline functions from this file, or you compile this file and link it
12 with other works to produce a work based on this file, this file
13 does not by itself cause the resulting work to be covered
14 by the GNU General Public License.
15
16 However the source code for this file must still be made available
17 in accordance with section (3) of the GNU General Public License.
18
19 This exception does not invalidate any other reasons why a work based
20 on this file might be covered by the GNU General Public License.
21 */
22
23 #include <string>
24 #include <sstream>
25 #include <stdexcept>
26 #include <iostream>
27
28 #include <boost/archive/binary_oarchive.hpp>
29 #include <boost/archive/binary_iarchive.hpp>
30 #include <boost/archive/xml_oarchive.hpp>
31 #include <boost/archive/xml_iarchive.hpp>
32 #include <boost/serialization/serialization.hpp>
33
34 #include <boost/bind.hpp>
35
36 #include "command_server.hxx"
37 #include "container.hxx"
38 #include "log.hxx"
39
40 #include <config.h>
41
42 using namespace std;
43
44 namespace libt2n
45 {
46
47 command_server::command_server(server& _s)
48     : s(_s), guard_handle(0)
49 {
50     // register callback
51     s.add_callback(new_connection,bind(&command_server::send_hello, boost::ref(*this), _1));
52 }
53
54 /**
55  * Destructor
56  */
57 command_server::~command_server()
58 {
59 }
60
61 /// send a hello message to a new connection
62 void command_server::send_hello(unsigned int conn_id)
63 {
64     server_connection* sc=s.get_connection(conn_id);
65
66     if (!sc)
67         return;         // connection not existing, so no hello
68
69     std::ostringstream hello;
70
71     hello << "T2Nv" << PROTOCOL_VERSION << ';';
72
73     int byteordercheck=1;
74     hello.write((char*)&byteordercheck,sizeof(byteordercheck));
75
76     hello << ';';
77
78     sc->write(hello.str());
79 }
80
81 /// handle a command including deserialization and answering
82 void command_server::handle_packet(const std::string& packet, server_connection* conn)
83 {
84     OBJLOGSTREAM(s,debug,"handling packet from connection " << conn->get_id());
85
86     // deserialize packet
87     std::istringstream ifs(packet);
88     boost::archive::binary_iarchive ia(ifs);
89     command_container ccont;
90     result_container res;
91
92     try
93     {
94         ia >> ccont;
95     }
96     catch(boost::archive::archive_exception &e)
97     {
98         std::ostringstream msg;
99         msg << "archive_exception while deserializing on server-side, "
100                "code " << e.code << " (" << e.what() << ")";
101         res.set_exception(new t2n_serialization_error(msg.str()));
102     }
103
104     if (!res.has_exception())
105     {
106         std::ostream* ostr;
107         if ((ostr=s.get_logstream(fulldebug))!=NULL)
108         {
109             (*ostr) << "decoded packet data: " << std::endl;
110             boost::archive::xml_oarchive xo(*ostr);
111             xo << BOOST_SERIALIZATION_NVP(ccont);
112         }
113
114         command* cmd=cast_command(ccont.get_command());
115
116         if (cmd)
117         {
118             try
119             {
120                 res.set_result((*cmd)());
121             }
122             catch (t2n_exception &e)
123                 { res.set_exception(e.clone()); }
124         }
125         else
126         {
127             std::ostringstream msg;
128             if (ccont.get_command()!=NULL)
129                 msg << "illegal command of type " << typeid(ccont.get_command()).name() << " called";
130             else
131                 msg << "NULL command called";
132             res.set_exception(new t2n_command_error(msg.str()));
133         }
134     }
135
136     std::ostringstream ofs;
137     boost::archive::binary_oarchive oa(ofs);
138
139     try
140     {
141         oa << res;
142     }
143     catch(boost::archive::archive_exception &e)
144     {
145         std::ostringstream msg;
146         msg << "archive_exception while serializing on server-side, "
147                "code " << e.code << " (" << e.what() << ")";
148         res.set_exception(new t2n_serialization_error(msg.str()));
149         oa << res;
150     }
151
152     std::ostream* ostr;
153     if ((ostr=s.get_logstream(fulldebug))!=NULL)
154     {
155         (*ostr) << "returning result, decoded data: " << std::endl;
156         boost::archive::xml_oarchive xo(*ostr);
157         xo << BOOST_SERIALIZATION_NVP(res);
158     }
159
160     conn->write(ofs.str());
161 }
162
163 /** @brief handle incoming commands
164     @param[in,out] usec_timeout wait until new data is found, max timeout usecs.
165             -1: wait endless, 0: instant return
166     @param[out] usec_timeout_remaining microseconds from the timeout that were not used
167 */
168 void command_server::handle(long long usec_timeout, long long* usec_timeout_remaining)
169 {
170     guard_handle++;
171
172     try
173     {
174         if (s.fill_buffer(usec_timeout,usec_timeout_remaining))
175         {
176             std::string packet;
177             unsigned int conn_id = 0;
178
179             while (s.get_packet(packet,conn_id))
180             {
181                 server_connection* conn=s.get_connection(conn_id);
182                 if (!conn)
183                     EXCEPTIONSTREAM(error,logic_error,"illegal connection id " << conn_id << " received");
184                 try
185                     { handle_packet(packet,conn); }
186                 catch (t2n_transfer_error &e)
187                 {
188                     // shut down a connection with transfer errors (usually write errors)
189                     conn->close();
190                 }
191             }
192         }
193     }
194     catch(...)
195     {
196         guard_handle--;
197         throw;
198     }
199     guard_handle--;
200
201     // don't call cleanup on re-entered handle-calls
202     if (guard_handle == 0)
203         s.cleanup();
204 }
205
206 }