Fawkes API  Fawkes Development Version
openprs_comm.cpp
1 
2 /***************************************************************************
3  * openprs_comm.cpp - OpenPRS communication wrapper for Fawkes
4  *
5  * Created: Mon Aug 18 14:55:51 2014
6  * Copyright 2014 Tim Niemueller [www.niemueller.de]
7  ****************************************************************************/
8 
9 /* This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version. A runtime exception applies to
13  * this software (see LICENSE.GPL_WRE file mentioned below for details).
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Library General Public License for more details.
19  *
20  * Read the full text in the LICENSE.GPL_WRE file in the doc directory.
21  */
22 
23 #include "openprs_comm.h"
24 #include "proc.h"
25 #include "openprs_server_proxy.h"
26 #include <core/exception.h>
27 #include <core/exceptions/system.h>
28 
29 #include <opaque-pub.h>
30 #include <mp-pub.h>
31 
32 #include <unistd.h>
33 
34 // these exist in libExtMP and are exported, but not mentioned in the header
35 extern "C" {
36  void send_message_string_socket(int socket, Symbol rec, PString message );
37  void broadcast_message_string_socket(int socket, PString message );
38  void multicast_message_string_socket(int socket, unsigned int nb_recs, Symbol *recs, PString message );
39 }
40 
41 namespace fawkes {
42 #if 0 /* just to make Emacs auto-indent happy */
43 }
44 #endif
45 
46 
47 /** @class OpenPRSComm <plugins/openprs/utils/openprs_comm.h>
48  * OpenPRS communication wrapper.
49  * This class provides communication facilities via the OpenPRS message passer
50  * as well as through the OpenPRS server proxy.
51  * @author Tim Niemueller
52  */
53 
54 /** Constructor.
55  * @param local_name the local name with which the communication wrapper will be
56  * registered to the message parser. This can be used to send messages from the
57  * kernel.
58  * @param hostname host where the message passer runs
59  * @param port TCP port where the message passer listens
60  * @param server_proxy server proxy to use to send commands to kernels
61  * @param logger logger for informational messages (optional)
62  */
63 OpenPRSComm::OpenPRSComm(const char *local_name,
64  const char *hostname, unsigned short port, OpenPRSServerProxy *server_proxy,
65  Logger *logger)
66  : name_(local_name), server_proxy_(server_proxy), logger_(logger),
67  io_service_work_(io_service_), sd_mp_socket_(io_service_)
68 
69 {
70  // Protocol MUST be STRINGS_PT or otherwise our message reception code will break
71  mp_socket_ = external_register_to_the_mp_host_prot(local_name, hostname, port, STRINGS_PT);
72  if (mp_socket_ == -1) {
73  throw Exception("Failed to connect to OpenPRS as '%s'", local_name);
74  }
75  io_service_thread_ = std::thread([this]() { this->io_service_.run(); });
76  sd_mp_socket_.assign(dup(mp_socket_));
77  start_recv();
78 }
79 
80 
81 /** Destructor. */
83 {
84  io_service_.stop();
85  io_service_thread_.join();
86  if (mp_socket_ >= 0) {
87  ::close(mp_socket_);
88  }
89 }
90 
91 
92 /** Send a message to an OpenPRS kernel.
93  * @param recipient OpenPRS kernel name to send to
94  * @param message message to send, cf. OpenPRS manual for valid messages
95  */
96 void
97 OpenPRSComm::send_message(const char *recipient, const char *message)
98 {
99  send_message_string_socket(mp_socket_, recipient, (char *)message);
100 }
101 
102 
103 /** Send a message to all OpenPRS kernels.
104  * @param message message to send, cf. OpenPRS manual for valid messages
105  */
106 void
107 OpenPRSComm::broadcast_message(const char *message)
108 {
109  broadcast_message_string_socket(mp_socket_, (char *)message);
110 }
111 
112 
113 /** Send a message to multiple OpenPRS kernel.
114  * @param recipients Vector of OpenPRS kernel names to send to
115  * @param message message to send, cf. OpenPRS manual for valid messages
116  */
117 void
118 OpenPRSComm::multicast_message(std::vector<const char *> &recipients, const char *message)
119 {
120  multicast_message_string_socket(mp_socket_, recipients.size(), &(recipients[0]), (char *)message);
121 }
122 
123 
124 /** Send a message to an OpenPRS kernel.
125  * @param recipient OpenPRS kernel name to send to
126  * @param message message to send, cf. OpenPRS manual for valid messages
127  */
128 void
129 OpenPRSComm::send_message(const std::string &recipient, const std::string &message)
130 {
131  send_message_string_socket(mp_socket_, recipient.c_str(), (char *)message.c_str());
132 }
133 
134 
135 /** Send a message to all OpenPRS kernels.
136  * @param message message to send, cf. OpenPRS manual for valid messages
137  */
138 void
139 OpenPRSComm::broadcast_message(const std::string &message)
140 {
141  broadcast_message_string_socket(mp_socket_, (char *)message.c_str());
142 }
143 
144 
145 /** Send a message to multiple OpenPRS kernel.
146  * @param recipients Vector of OpenPRS kernel names to send to
147  * @param message message to send, cf. OpenPRS manual for valid messages
148  */
149 void
150 OpenPRSComm::multicast_message(const std::vector<std::string> &recipients, const std::string &message)
151 {
152  std::vector<const char *> recs;
153  recs.resize(recipients.size());
154  for (size_t i = 0; i < recipients.size(); ++i) {
155  recs[i] = recipients[i].c_str();
156  }
157  multicast_message_string_socket(mp_socket_, recs.size(), &(recs[0]), (char *)message.c_str());
158 }
159 
160 
161 /** Send a formatted message to an OpenPRS kernel.
162  * @param recipient OpenPRS kernel name to send to
163  * @param format format for message to send according to sprintf()
164  */
165 void
166 OpenPRSComm::send_message_f(const std::string &recipient, const char *format, ...)
167 {
168  va_list arg;
169  va_start(arg, format);
170  char *msg;
171  if (vasprintf(&msg, format, arg) == -1) {
172  throw OutOfMemoryException("Cannot format OpenPRS client command string");
173  }
174  va_end(arg);
175  send_message_string_socket(mp_socket_, recipient.c_str(), msg);
176  free(msg);
177 }
178 
179 
180 /** Send a formatted message to all OpenPRS kernels.
181  * @param format format for message to send according to sprintf()
182  */
183 void
184 OpenPRSComm::broadcast_message_f(const char *format, ...)
185 {
186  va_list arg;
187  va_start(arg, format);
188  char *msg;
189  if (vasprintf(&msg, format, arg) == -1) {
190  throw OutOfMemoryException("Cannot format OpenPRS client command string");
191  }
192  va_end(arg);
193  broadcast_message_string_socket(mp_socket_, msg);
194  free(msg);
195 }
196 
197 
198 /** Send a message to multiple OpenPRS kernel.
199  * @param recipients Vector of OpenPRS kernel names to send to
200  * @param format format for message to send according to sprintf()
201  */
202 void
203 OpenPRSComm::multicast_message_f(const std::vector<std::string> &recipients, const char *format, ...)
204 {
205  std::vector<const char *> recs;
206  recs.resize(recipients.size());
207  for (size_t i = 0; i < recipients.size(); ++i) {
208  recs[i] = recipients[i].c_str();
209  }
210  va_list arg;
211  va_start(arg, format);
212  char *msg;
213  if (vasprintf(&msg, format, arg) == -1) {
214  throw OutOfMemoryException("Cannot format OpenPRS client command string");
215  }
216  va_end(arg);
217  multicast_message_string_socket(mp_socket_, recs.size(), &(recs[0]), msg);
218  free(msg);
219 }
220 
221 
222 /** Transmit a command to an OpenPRS kernel.
223  * This works equivalent to the transmit oprs-server console command.
224  * @param recipient OpenPRS kernel name to send to
225  * @param message command to send, cf. OpenPRS manual for valid commands
226  */
227 void
228 OpenPRSComm::transmit_command(const char *recipient, const char *message)
229 {
230  server_proxy_->transmit_command(recipient, message);
231 }
232 
233 /** Transmit a command to an OpenPRS kernel.
234  * This works equivalent to the transmit oprs-server console command.
235  * @param recipient OpenPRS kernel name to send to
236  * @param message command to send, cf. OpenPRS manual for valid commands
237  */
238 void
239 OpenPRSComm::transmit_command(const std::string &recipient, const std::string &message)
240 {
241  server_proxy_->transmit_command(recipient, message);
242 }
243 
244 
245 /** Transmit a command to an OpenPRS kernel.
246  * This works equivalent to the transmit oprs-server console command.
247  * This function allows to pass a format according to the sprintf()
248  * format and its arguments.
249  * @param recipient OpenPRS kernel name to send to
250  * @param format format string for the command, must be followed by the
251  * appropriate number and types of arguments.
252  */
253 void
254 OpenPRSComm::transmit_command_f(const std::string &recipient, const char *format, ...)
255 {
256  va_list arg;
257  va_start(arg, format);
258  server_proxy_->transmit_command_v(recipient, format, arg);
259  va_end(arg);
260 }
261 
262 
263 void
264 OpenPRSComm::start_recv()
265 {
266  sd_mp_socket_.async_read_some(boost::asio::null_buffers(),
267  boost::bind(&OpenPRSComm::handle_recv, this,
268  boost::asio::placeholders::error));
269 }
270 
271 
272 void
273 OpenPRSComm::handle_recv(const boost::system::error_code &err)
274 {
275  if (! err) {
276  try {
277  std::string sender = read_string_from_socket(sd_mp_socket_);
278  std::string message = read_string_from_socket(sd_mp_socket_);
279 
280  sig_rcvd_(sender, message);
281  } catch (Exception &e) {
282  if (logger_) {
283  logger_->log_warn(name_.c_str(), "Failed to receive message: %s", e.what_no_backtrace());
284  }
285  }
286  } else if (logger_) {
287  logger_->log_warn(name_.c_str(), "Failed to receive message: %s", err.message().c_str());
288  }
289  start_recv();
290 }
291 
292 
293 std::string
294 OpenPRSComm::read_string_from_socket(boost::asio::posix::stream_descriptor &socket)
295 {
296  uint32_t s_size = 0;
297  boost::system::error_code ec;
298  boost::asio::read(socket, boost::asio::buffer(&s_size, sizeof(s_size)), ec);
299  if (ec) {
300  throw Exception("Failed to read string size from socket: %s", ec.message().c_str());
301  }
302  s_size = ntohl(s_size);
303 
304  char s[s_size + 1];
305  boost::asio::read(socket, boost::asio::buffer(s, s_size), ec);
306  if (ec) {
307  throw Exception("Failed to read string content from socket: %s", ec.message().c_str());
308  }
309  s[s_size] = 0;
310 
311  return s;
312 }
313 
314 
315 } // end namespace fawkes
void transmit_command_v(const std::string &client_name, const char *format, va_list arg)
Transmit a command to an OpenPRS kernel.
virtual ~OpenPRSComm()
Destructor.
Proxy for the OpenPRS server communication.
Fawkes library namespace.
void transmit_command(const std::string &client_name, const std::string &command)
Transmit a command to an OpenPRS kernel.
void multicast_message_f(const std::vector< std::string > &recipients, const char *format,...)
Send a message to multiple OpenPRS kernel.
void transmit_command(const char *recipient, const char *message)
Transmit a command to an OpenPRS kernel.
void broadcast_message(const char *message)
Send a message to all OpenPRS kernels.
Base class for exceptions in Fawkes.
Definition: exception.h:36
void send_message_f(const std::string &recipient, const char *format,...)
Send a formatted message to an OpenPRS kernel.
virtual const char * what_no_backtrace() const
Get primary string (does not implicitly print the back trace).
Definition: exception.cpp:686
virtual void log_warn(const char *component, const char *format,...)=0
Log warning message.
void broadcast_message_f(const char *format,...)
Send a formatted message to all OpenPRS kernels.
void transmit_command_f(const std::string &recipient, const char *format,...)
Transmit a command to an OpenPRS kernel.
void multicast_message(std::vector< const char *> &recipients, const char *message)
Send a message to multiple OpenPRS kernel.
void send_message(const char *recipient, const char *message)
Send a message to an OpenPRS kernel.
OpenPRSComm(const char *local_name, const char *hostname, unsigned short port, OpenPRSServerProxy *server_proxy, Logger *logger=NULL)
Constructor.
System ran out of memory and desired operation could not be fulfilled.
Definition: system.h:32
Interface for logging.
Definition: logger.h:34