consolidate reclaim attribute lib
[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   /**
43    * Our control connection to the ARM service.
44    */
45   struct GNUNET_MQ_Handle *mq;
46
47   /**
48    * The configuration that we are using.
49    */
50   const struct GNUNET_CONFIGURATION_Handle *cfg;
51
52   /**
53    * ID of the reconnect task (if any).
54    */
55   struct GNUNET_SCHEDULER_Task *reconnect_task;
56
57   /**
58    * Current delay we use for re-trying to connect to core.
59    */
60   struct GNUNET_TIME_Relative retry_backoff;
61
62   /**
63    * Callback to invoke on status updates.
64    */
65   GNUNET_ARM_ServiceMonitorCallback service_status;
66
67   /**
68    * Closure for @e service_status.
69    */
70   void *service_status_cls;
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 = GNUNET_SCHEDULER_add_delayed (h->retry_backoff,
117                                                     &reconnect_arm_monitor_task,
118                                                     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, const struct GNUNET_ARM_StatusMessage *msg)
132 {
133   size_t sl =
134     ntohs (msg->header.size) - sizeof(struct GNUNET_ARM_StatusMessage);
135   const char *name = (const char *) &msg[1];
136
137   (void) cls;
138   if ((0 == sl) || ('\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, const struct GNUNET_ARM_StatusMessage *res)
155 {
156   struct GNUNET_ARM_MonitorHandle *h = cls;
157   enum GNUNET_ARM_ServiceMonitorStatus status;
158
159   status = (enum GNUNET_ARM_ServiceMonitorStatus) ntohl (res->status);
160   LOG (GNUNET_ERROR_TYPE_DEBUG,
161        "Received notification from ARM for service `%s' with status %d\n",
162        (const char *) &res[1],
163        (int) status);
164   if (NULL != h->service_status)
165     h->service_status (h->service_status_cls, (const char *) &res[1], status);
166 }
167
168
169 /**
170  * Generic error handler, called with the appropriate error code and
171  * the same closure specified at the creation of the message queue.
172  * Not every message queue implementation supports an error handler.
173  *
174  * @param cls closure with the `struct GNUNET_ARM_MonitorHandle *`
175  * @param error error code
176  */
177 static void
178 mq_error_handler (void *cls, enum GNUNET_MQ_Error error)
179 {
180   struct GNUNET_ARM_MonitorHandle *h = cls;
181
182   (void) error;
183   reconnect_arm_monitor_later (h);
184 }
185
186
187 /**
188  * Connect to the ARM service for monitoring.
189  *
190  * @param h handle to connect
191  * @return #GNUNET_OK on success
192  */
193 static int
194 reconnect_arm_monitor (struct GNUNET_ARM_MonitorHandle *h)
195 {
196   struct GNUNET_MQ_MessageHandler handlers[] =
197   { GNUNET_MQ_hd_var_size (monitor_notify,
198                            GNUNET_MESSAGE_TYPE_ARM_STATUS,
199                            struct GNUNET_ARM_StatusMessage,
200                            h),
201     GNUNET_MQ_handler_end () };
202   struct GNUNET_MessageHeader *msg;
203   struct GNUNET_MQ_Envelope *env;
204
205   GNUNET_assert (NULL == h->mq);
206   h->mq = GNUNET_CLIENT_connect (h->cfg, "arm", handlers, &mq_error_handler, h);
207   if (NULL == h->mq)
208   {
209     if (NULL != h->service_status)
210       h->service_status (h->service_status_cls,
211                          NULL,
212                          GNUNET_ARM_SERVICE_STOPPED);
213     return GNUNET_SYSERR;
214   }
215   env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_ARM_MONITOR);
216   GNUNET_MQ_send (h->mq, env);
217   return GNUNET_OK;
218 }
219
220
221 /**
222  * Setup a context for monitoring ARM, then
223  * start connecting to the ARM service for monitoring using that context.
224  *
225  * @param cfg configuration to use (needed to contact ARM;
226  *        the ARM service may internally use a different
227  *        configuration to determine how to start the service).
228  * @param cont callback to invoke on status updates
229  * @param cont_cls closure for @a cont
230  * @return context to use for further ARM monitor operations, NULL on error.
231  */
232 struct GNUNET_ARM_MonitorHandle *
233 GNUNET_ARM_monitor_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
234                           GNUNET_ARM_ServiceMonitorCallback cont,
235                           void *cont_cls)
236 {
237   struct GNUNET_ARM_MonitorHandle *h;
238
239   h = GNUNET_new (struct GNUNET_ARM_MonitorHandle);
240   h->cfg = cfg;
241   h->service_status = cont;
242   h->service_status_cls = cont_cls;
243   if (GNUNET_OK != reconnect_arm_monitor (h))
244   {
245     GNUNET_free (h);
246     return NULL;
247   }
248   return h;
249 }
250
251
252 /**
253  * Disconnect from the ARM service (if connected) and destroy the context.
254  *
255  * @param h the handle that was being used
256  */
257 void
258 GNUNET_ARM_monitor_stop (struct GNUNET_ARM_MonitorHandle *h)
259 {
260   if (NULL != h->mq)
261   {
262     GNUNET_MQ_destroy (h->mq);
263     h->mq = NULL;
264   }
265   if (NULL != h->reconnect_task)
266   {
267     GNUNET_SCHEDULER_cancel (h->reconnect_task);
268     h->reconnect_task = NULL;
269   }
270   GNUNET_free (h);
271 }
272
273
274 /* end of arm_api.c */