REST/NAMESTORE: rework API
[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   /**
44    * Our control connection to the ARM service.
45    */
46   struct GNUNET_MQ_Handle *mq;
47
48   /**
49    * The configuration that we are using.
50    */
51   const struct GNUNET_CONFIGURATION_Handle *cfg;
52
53   /**
54    * ID of the reconnect task (if any).
55    */
56   struct GNUNET_SCHEDULER_Task *reconnect_task;
57
58   /**
59    * Current delay we use for re-trying to connect to core.
60    */
61   struct GNUNET_TIME_Relative retry_backoff;
62
63   /**
64    * Callback to invoke on status updates.
65    */
66   GNUNET_ARM_ServiceStatusCallback service_status;
67
68   /**
69    * Closure for @e service_status.
70    */
71   void *service_status_cls;
72
73 };
74
75
76 /**
77  * Connect to the ARM service for monitoring.
78  *
79  * @param h handle to connect
80  * @return #GNUNET_OK on success
81  */
82 static int
83 reconnect_arm_monitor (struct GNUNET_ARM_MonitorHandle *h);
84
85
86 /**
87  * Task scheduled to try to re-connect to arm.
88  *
89  * @param cls the `struct GNUNET_ARM_MonitorHandle`
90  */
91 static void
92 reconnect_arm_monitor_task (void *cls)
93 {
94   struct GNUNET_ARM_MonitorHandle *h = cls;
95
96   h->reconnect_task = NULL;
97   LOG (GNUNET_ERROR_TYPE_DEBUG,
98        "Connecting to ARM service for monitoring after delay\n");
99   GNUNET_break (GNUNET_OK == reconnect_arm_monitor (h));
100 }
101
102
103 /**
104  * Close down any existing connection to the ARM service and
105  * try re-establishing it later.
106  *
107  * @param h our handle
108  */
109 static void
110 reconnect_arm_monitor_later (struct GNUNET_ARM_MonitorHandle *h)
111 {
112   if (NULL != h->mq)
113   {
114     GNUNET_MQ_destroy (h->mq);
115     h->mq = NULL;
116   }
117   GNUNET_assert (NULL == h->reconnect_task);
118   h->reconnect_task
119     = GNUNET_SCHEDULER_add_delayed (h->retry_backoff,
120                                     &reconnect_arm_monitor_task, h);
121   h->retry_backoff = GNUNET_TIME_STD_BACKOFF (h->retry_backoff);
122 }
123
124
125 /**
126  * Check notification messages received from ARM is well-formed.
127  *
128  * @param cls our `struct GNUNET_ARM_MonitorHandle`
129  * @param msg the message received from the arm service
130  * @return #GNUNET_OK if the message is well-formed
131  */
132 static int
133 check_monitor_notify (void *cls,
134                        const struct GNUNET_ARM_StatusMessage *msg)
135 {
136   size_t sl = ntohs (msg->header.size) - sizeof (struct GNUNET_ARM_StatusMessage);
137   const char *name = (const char *) &msg[1];
138
139   if ( (0 == sl) ||
140        ('\0' != name[sl-1]) )
141   {
142     GNUNET_break (0);
143     return GNUNET_SYSERR;
144   }
145   return GNUNET_OK;
146 }
147
148
149 /**
150  * Handler for notification messages received from ARM.
151  *
152  * @param cls our `struct GNUNET_ARM_MonitorHandle`
153  * @param res the message received from the arm service
154  */
155 static void
156 handle_monitor_notify (void *cls,
157                        const struct GNUNET_ARM_StatusMessage *res)
158 {
159   struct GNUNET_ARM_MonitorHandle *h = cls;
160   enum GNUNET_ARM_ServiceStatus status;
161
162   status = (enum GNUNET_ARM_ServiceStatus) ntohl (res->status);
163   LOG (GNUNET_ERROR_TYPE_DEBUG,
164        "Received notification from ARM for service `%s' with status %d\n",
165        (const char *) &res[1],
166        (int) status);
167   if (NULL != h->service_status)
168     h->service_status (h->service_status_cls,
169                        (const char *) &res[1],
170                        status);
171 }
172
173
174 /**
175  * Generic error handler, called with the appropriate error code and
176  * the same closure specified at the creation of the message queue.
177  * Not every message queue implementation supports an error handler.
178  *
179  * @param cls closure with the `struct GNUNET_ARM_MonitorHandle *`
180  * @param error error code
181  */
182 static void
183 mq_error_handler (void *cls,
184                   enum GNUNET_MQ_Error error)
185 {
186   struct GNUNET_ARM_MonitorHandle *h = cls;
187
188   reconnect_arm_monitor_later (h);
189 }
190
191
192 /**
193  * Connect to the ARM service for monitoring.
194  *
195  * @param h handle to connect
196  * @return #GNUNET_OK on success
197  */
198 static int
199 reconnect_arm_monitor (struct GNUNET_ARM_MonitorHandle *h)
200 {
201   struct GNUNET_MQ_MessageHandler handlers[] = {
202     GNUNET_MQ_hd_var_size (monitor_notify,
203                            GNUNET_MESSAGE_TYPE_ARM_STATUS,
204                            struct GNUNET_ARM_StatusMessage,
205                            h),
206     GNUNET_MQ_handler_end ()
207   };
208   struct GNUNET_MessageHeader *msg;
209   struct GNUNET_MQ_Envelope *env;
210
211   GNUNET_assert (NULL == h->mq);
212   h->mq = GNUNET_CLIENT_connect (h->cfg,
213                                  "arm",
214                                  handlers,
215                                  &mq_error_handler,
216                                  h);
217   if (NULL == h->mq)
218   {
219     if (NULL != h->service_status)
220       h->service_status (h->service_status_cls,
221                          NULL,
222                          GNUNET_ARM_SERVICE_STOPPED);
223     return GNUNET_SYSERR;
224   }
225   env = GNUNET_MQ_msg (msg,
226                        GNUNET_MESSAGE_TYPE_ARM_MONITOR);
227   GNUNET_MQ_send (h->mq,
228                   env);
229   return GNUNET_OK;
230 }
231
232
233 /**
234  * Setup a context for monitoring ARM, then
235  * start connecting to the ARM service for monitoring using that context.
236  *
237  * @param cfg configuration to use (needed to contact ARM;
238  *        the ARM service may internally use a different
239  *        configuration to determine how to start the service).
240  * @param cont callback to invoke on status updates
241  * @param cont_cls closure for @a cont
242  * @return context to use for further ARM monitor operations, NULL on error.
243  */
244 struct GNUNET_ARM_MonitorHandle *
245 GNUNET_ARM_monitor_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
246                           GNUNET_ARM_ServiceStatusCallback cont,
247                           void *cont_cls)
248 {
249   struct GNUNET_ARM_MonitorHandle *h;
250
251   h = GNUNET_new (struct GNUNET_ARM_MonitorHandle);
252   h->cfg = cfg;
253   h->service_status = cont;
254   h->service_status_cls = cont_cls;
255   if (GNUNET_OK != reconnect_arm_monitor (h))
256   {
257     GNUNET_free (h);
258     return NULL;
259   }
260   return h;
261 }
262
263
264 /**
265  * Disconnect from the ARM service (if connected) and destroy the context.
266  *
267  * @param h the handle that was being used
268  */
269 void
270 GNUNET_ARM_monitor_stop (struct GNUNET_ARM_MonitorHandle *h)
271 {
272   if (NULL != h->mq)
273   {
274     GNUNET_MQ_destroy (h->mq);
275     h->mq = NULL;
276   }
277   if (NULL != h->reconnect_task)
278   {
279     GNUNET_SCHEDULER_cancel (h->reconnect_task);
280     h->reconnect_task = NULL;
281   }
282   GNUNET_free (h);
283 }
284
285
286 /* end of arm_api.c */