Merge branch 'master' of ssh://git.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      SPDX-License-Identifier: AGPL3.0-or-later
19  */
20
21 /**
22  * @file arm/arm_monitor_api.c
23  * @brief API for monitoring the ARM service
24  * @author Christian Grothoff
25  * @author LRN
26  */
27 #include "platform.h"
28 #include "gnunet_arm_service.h"
29 #include "gnunet_util_lib.h"
30 #include "gnunet_protocols.h"
31 #include "arm.h"
32
33 #define INIT_TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 5)
34
35 #define LOG(kind, ...) GNUNET_log_from(kind, "arm-monitor-api", __VA_ARGS__)
36
37 /**
38  * Handle for interacting with ARM.
39  */
40 struct GNUNET_ARM_MonitorHandle {
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_ServiceMonitorCallback service_status;
65
66   /**
67    * Closure for @e service_status.
68    */
69   void *service_status_cls;
70 };
71
72
73 /**
74  * Connect to the ARM service for monitoring.
75  *
76  * @param h handle to connect
77  * @return #GNUNET_OK on success
78  */
79 static int
80 reconnect_arm_monitor(struct GNUNET_ARM_MonitorHandle *h);
81
82
83 /**
84  * Task scheduled to try to re-connect to arm.
85  *
86  * @param cls the `struct GNUNET_ARM_MonitorHandle`
87  */
88 static void
89 reconnect_arm_monitor_task(void *cls)
90 {
91   struct GNUNET_ARM_MonitorHandle *h = cls;
92
93   h->reconnect_task = NULL;
94   LOG(GNUNET_ERROR_TYPE_DEBUG,
95       "Connecting to ARM service for monitoring after delay\n");
96   GNUNET_break(GNUNET_OK == reconnect_arm_monitor(h));
97 }
98
99
100 /**
101  * Close down any existing connection to the ARM service and
102  * try re-establishing it later.
103  *
104  * @param h our handle
105  */
106 static void
107 reconnect_arm_monitor_later(struct GNUNET_ARM_MonitorHandle *h)
108 {
109   if (NULL != h->mq)
110     {
111       GNUNET_MQ_destroy(h->mq);
112       h->mq = NULL;
113     }
114   GNUNET_assert(NULL == h->reconnect_task);
115   h->reconnect_task = GNUNET_SCHEDULER_add_delayed(h->retry_backoff,
116                                                    &reconnect_arm_monitor_task,
117                                                    h);
118   h->retry_backoff = GNUNET_TIME_STD_BACKOFF(h->retry_backoff);
119 }
120
121
122 /**
123  * Check notification messages received from ARM is well-formed.
124  *
125  * @param cls our `struct GNUNET_ARM_MonitorHandle`
126  * @param msg the message received from the arm service
127  * @return #GNUNET_OK if the message is well-formed
128  */
129 static int
130 check_monitor_notify(void *cls, const struct GNUNET_ARM_StatusMessage *msg)
131 {
132   size_t sl =
133     ntohs(msg->header.size) - sizeof(struct GNUNET_ARM_StatusMessage);
134   const char *name = (const char *)&msg[1];
135
136   (void)cls;
137   if ((0 == sl) || ('\0' != name[sl - 1]))
138     {
139       GNUNET_break(0);
140       return GNUNET_SYSERR;
141     }
142   return GNUNET_OK;
143 }
144
145
146 /**
147  * Handler for notification messages received from ARM.
148  *
149  * @param cls our `struct GNUNET_ARM_MonitorHandle`
150  * @param res the message received from the arm service
151  */
152 static void
153 handle_monitor_notify(void *cls, const struct GNUNET_ARM_StatusMessage *res)
154 {
155   struct GNUNET_ARM_MonitorHandle *h = cls;
156   enum GNUNET_ARM_ServiceMonitorStatus status;
157
158   status = (enum GNUNET_ARM_ServiceMonitorStatus)ntohl(res->status);
159   LOG(GNUNET_ERROR_TYPE_DEBUG,
160       "Received notification from ARM for service `%s' with status %d\n",
161       (const char *)&res[1],
162       (int)status);
163   if (NULL != h->service_status)
164     h->service_status(h->service_status_cls, (const char *)&res[1], status);
165 }
166
167
168 /**
169  * Generic error handler, called with the appropriate error code and
170  * the same closure specified at the creation of the message queue.
171  * Not every message queue implementation supports an error handler.
172  *
173  * @param cls closure with the `struct GNUNET_ARM_MonitorHandle *`
174  * @param error error code
175  */
176 static void
177 mq_error_handler(void *cls, enum GNUNET_MQ_Error error)
178 {
179   struct GNUNET_ARM_MonitorHandle *h = cls;
180
181   (void)error;
182   reconnect_arm_monitor_later(h);
183 }
184
185
186 /**
187  * Connect to the ARM service for monitoring.
188  *
189  * @param h handle to connect
190  * @return #GNUNET_OK on success
191  */
192 static int
193 reconnect_arm_monitor(struct GNUNET_ARM_MonitorHandle *h)
194 {
195   struct GNUNET_MQ_MessageHandler handlers[] =
196   { GNUNET_MQ_hd_var_size(monitor_notify,
197                           GNUNET_MESSAGE_TYPE_ARM_STATUS,
198                           struct GNUNET_ARM_StatusMessage,
199                           h),
200     GNUNET_MQ_handler_end() };
201   struct GNUNET_MessageHeader *msg;
202   struct GNUNET_MQ_Envelope *env;
203
204   GNUNET_assert(NULL == h->mq);
205   h->mq = GNUNET_CLIENT_connect(h->cfg, "arm", handlers, &mq_error_handler, h);
206   if (NULL == h->mq)
207     {
208       if (NULL != h->service_status)
209         h->service_status(h->service_status_cls,
210                           NULL,
211                           GNUNET_ARM_SERVICE_STOPPED);
212       return GNUNET_SYSERR;
213     }
214   env = GNUNET_MQ_msg(msg, GNUNET_MESSAGE_TYPE_ARM_MONITOR);
215   GNUNET_MQ_send(h->mq, env);
216   return GNUNET_OK;
217 }
218
219
220 /**
221  * Setup a context for monitoring ARM, then
222  * start connecting to the ARM service for monitoring using that context.
223  *
224  * @param cfg configuration to use (needed to contact ARM;
225  *        the ARM service may internally use a different
226  *        configuration to determine how to start the service).
227  * @param cont callback to invoke on status updates
228  * @param cont_cls closure for @a cont
229  * @return context to use for further ARM monitor operations, NULL on error.
230  */
231 struct GNUNET_ARM_MonitorHandle *
232 GNUNET_ARM_monitor_start(const struct GNUNET_CONFIGURATION_Handle *cfg,
233                          GNUNET_ARM_ServiceMonitorCallback cont,
234                          void *cont_cls)
235 {
236   struct GNUNET_ARM_MonitorHandle *h;
237
238   h = GNUNET_new(struct GNUNET_ARM_MonitorHandle);
239   h->cfg = cfg;
240   h->service_status = cont;
241   h->service_status_cls = cont_cls;
242   if (GNUNET_OK != reconnect_arm_monitor(h))
243     {
244       GNUNET_free(h);
245       return NULL;
246     }
247   return h;
248 }
249
250
251 /**
252  * Disconnect from the ARM service (if connected) and destroy the context.
253  *
254  * @param h the handle that was being used
255  */
256 void
257 GNUNET_ARM_monitor_stop(struct GNUNET_ARM_MonitorHandle *h)
258 {
259   if (NULL != h->mq)
260     {
261       GNUNET_MQ_destroy(h->mq);
262       h->mq = NULL;
263     }
264   if (NULL != h->reconnect_task)
265     {
266       GNUNET_SCHEDULER_cancel(h->reconnect_task);
267       h->reconnect_task = NULL;
268     }
269   GNUNET_free(h);
270 }
271
272
273 /* end of arm_api.c */