Merge branch 'master' of gnunet.org:gnunet
[oweals/gnunet.git] / src / arm / arm_monitor_api.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009, 2010, 2012, 2013, 2016 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      Affero General Public License for more details.
14     
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /**
20  * @file arm/arm_monitor_api.c
21  * @brief API for monitoring the ARM service
22  * @author Christian Grothoff
23  * @author LRN
24  */
25 #include "platform.h"
26 #include "gnunet_arm_service.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_protocols.h"
29 #include "arm.h"
30
31 #define INIT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
32
33 #define LOG(kind,...) GNUNET_log_from (kind, "arm-monitor-api",__VA_ARGS__)
34
35 /**
36  * Handle for interacting with ARM.
37  */
38 struct GNUNET_ARM_MonitorHandle
39 {
40
41   /**
42    * Our control connection to the ARM service.
43    */
44   struct GNUNET_MQ_Handle *mq;
45
46   /**
47    * The configuration that we are using.
48    */
49   const struct GNUNET_CONFIGURATION_Handle *cfg;
50
51   /**
52    * ID of the reconnect task (if any).
53    */
54   struct GNUNET_SCHEDULER_Task *reconnect_task;
55
56   /**
57    * Current delay we use for re-trying to connect to core.
58    */
59   struct GNUNET_TIME_Relative retry_backoff;
60
61   /**
62    * Callback to invoke on status updates.
63    */
64   GNUNET_ARM_ServiceStatusCallback service_status;
65
66   /**
67    * Closure for @e service_status.
68    */
69   void *service_status_cls;
70
71 };
72
73
74 /**
75  * Connect to the ARM service for monitoring.
76  *
77  * @param h handle to connect
78  * @return #GNUNET_OK on success
79  */
80 static int
81 reconnect_arm_monitor (struct GNUNET_ARM_MonitorHandle *h);
82
83
84 /**
85  * Task scheduled to try to re-connect to arm.
86  *
87  * @param cls the `struct GNUNET_ARM_MonitorHandle`
88  */
89 static void
90 reconnect_arm_monitor_task (void *cls)
91 {
92   struct GNUNET_ARM_MonitorHandle *h = cls;
93
94   h->reconnect_task = NULL;
95   LOG (GNUNET_ERROR_TYPE_DEBUG,
96        "Connecting to ARM service for monitoring after delay\n");
97   GNUNET_break (GNUNET_OK == reconnect_arm_monitor (h));
98 }
99
100
101 /**
102  * Close down any existing connection to the ARM service and
103  * try re-establishing it later.
104  *
105  * @param h our handle
106  */
107 static void
108 reconnect_arm_monitor_later (struct GNUNET_ARM_MonitorHandle *h)
109 {
110   if (NULL != h->mq)
111   {
112     GNUNET_MQ_destroy (h->mq);
113     h->mq = NULL;
114   }
115   GNUNET_assert (NULL == h->reconnect_task);
116   h->reconnect_task
117     = GNUNET_SCHEDULER_add_delayed (h->retry_backoff,
118                                     &reconnect_arm_monitor_task, h);
119   h->retry_backoff = GNUNET_TIME_STD_BACKOFF (h->retry_backoff);
120 }
121
122
123 /**
124  * Check notification messages received from ARM is well-formed.
125  *
126  * @param cls our `struct GNUNET_ARM_MonitorHandle`
127  * @param msg the message received from the arm service
128  * @return #GNUNET_OK if the message is well-formed
129  */
130 static int
131 check_monitor_notify (void *cls,
132                        const struct GNUNET_ARM_StatusMessage *msg)
133 {
134   size_t sl = ntohs (msg->header.size) - sizeof (struct GNUNET_ARM_StatusMessage);
135   const char *name = (const char *) &msg[1];
136
137   if ( (0 == sl) ||
138        ('\0' != name[sl-1]) )
139   {
140     GNUNET_break (0);
141     return GNUNET_SYSERR;
142   }
143   return GNUNET_OK;
144 }
145
146
147 /**
148  * Handler for notification messages received from ARM.
149  *
150  * @param cls our `struct GNUNET_ARM_MonitorHandle`
151  * @param res the message received from the arm service
152  */
153 static void
154 handle_monitor_notify (void *cls,
155                        const struct GNUNET_ARM_StatusMessage *res)
156 {
157   struct GNUNET_ARM_MonitorHandle *h = cls;
158   enum GNUNET_ARM_ServiceStatus status;
159
160   status = (enum GNUNET_ARM_ServiceStatus) ntohl (res->status);
161   LOG (GNUNET_ERROR_TYPE_DEBUG,
162        "Received notification from ARM for service `%s' with status %d\n",
163        (const char *) &res[1],
164        (int) status);
165   if (NULL != h->service_status)
166     h->service_status (h->service_status_cls,
167                        (const char *) &res[1],
168                        status);
169 }
170
171
172 /**
173  * Generic error handler, called with the appropriate error code and
174  * the same closure specified at the creation of the message queue.
175  * Not every message queue implementation supports an error handler.
176  *
177  * @param cls closure with the `struct GNUNET_ARM_MonitorHandle *`
178  * @param error error code
179  */
180 static void
181 mq_error_handler (void *cls,
182                   enum GNUNET_MQ_Error error)
183 {
184   struct GNUNET_ARM_MonitorHandle *h = cls;
185
186   reconnect_arm_monitor_later (h);
187 }
188
189
190 /**
191  * Connect to the ARM service for monitoring.
192  *
193  * @param h handle to connect
194  * @return #GNUNET_OK on success
195  */
196 static int
197 reconnect_arm_monitor (struct GNUNET_ARM_MonitorHandle *h)
198 {
199   struct GNUNET_MQ_MessageHandler handlers[] = {
200     GNUNET_MQ_hd_var_size (monitor_notify,
201                            GNUNET_MESSAGE_TYPE_ARM_STATUS,
202                            struct GNUNET_ARM_StatusMessage,
203                            h),
204     GNUNET_MQ_handler_end ()
205   };
206   struct GNUNET_MessageHeader *msg;
207   struct GNUNET_MQ_Envelope *env;
208
209   GNUNET_assert (NULL == h->mq);
210   h->mq = GNUNET_CLIENT_connect (h->cfg,
211                                  "arm",
212                                  handlers,
213                                  &mq_error_handler,
214                                  h);
215   if (NULL == h->mq)
216   {
217     if (NULL != h->service_status)
218       h->service_status (h->service_status_cls,
219                          NULL,
220                          GNUNET_ARM_SERVICE_STOPPED);
221     return GNUNET_SYSERR;
222   }
223   env = GNUNET_MQ_msg (msg,
224                        GNUNET_MESSAGE_TYPE_ARM_MONITOR);
225   GNUNET_MQ_send (h->mq,
226                   env);
227   return GNUNET_OK;
228 }
229
230
231 /**
232  * Setup a context for monitoring ARM, then
233  * start connecting to the ARM service for monitoring using that context.
234  *
235  * @param cfg configuration to use (needed to contact ARM;
236  *        the ARM service may internally use a different
237  *        configuration to determine how to start the service).
238  * @param cont callback to invoke on status updates
239  * @param cont_cls closure for @a cont
240  * @return context to use for further ARM monitor operations, NULL on error.
241  */
242 struct GNUNET_ARM_MonitorHandle *
243 GNUNET_ARM_monitor_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
244                           GNUNET_ARM_ServiceStatusCallback cont,
245                           void *cont_cls)
246 {
247   struct GNUNET_ARM_MonitorHandle *h;
248
249   h = GNUNET_new (struct GNUNET_ARM_MonitorHandle);
250   h->cfg = cfg;
251   h->service_status = cont;
252   h->service_status_cls = cont_cls;
253   if (GNUNET_OK != reconnect_arm_monitor (h))
254   {
255     GNUNET_free (h);
256     return NULL;
257   }
258   return h;
259 }
260
261
262 /**
263  * Disconnect from the ARM service (if connected) and destroy the context.
264  *
265  * @param h the handle that was being used
266  */
267 void
268 GNUNET_ARM_monitor_stop (struct GNUNET_ARM_MonitorHandle *h)
269 {
270   if (NULL != h->mq)
271   {
272     GNUNET_MQ_destroy (h->mq);
273     h->mq = NULL;
274   }
275   if (NULL != h->reconnect_task)
276   {
277     GNUNET_SCHEDULER_cancel (h->reconnect_task);
278     h->reconnect_task = NULL;
279   }
280   GNUNET_free (h);
281 }
282
283
284 /* end of arm_api.c */