redundant
[oweals/gnunet.git] / src / arm / arm_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 2, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file arm/arm_api.c
23  * @brief API for accessing the ARM service
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_arm_service.h"
28 #include "gnunet_client_lib.h"
29 #include "gnunet_getopt_lib.h"
30 #include "gnunet_os_lib.h"
31 #include "gnunet_protocols.h"
32 #include "gnunet_server_lib.h"
33 #include "arm.h"
34
35
36 /**
37  * FIXME: document.
38  */
39 struct ArmContext
40 {
41
42   /**
43    * FIXME: document.
44    */
45   GNUNET_ARM_Callback callback;
46
47   /**
48    * FIXME: document.
49    */
50   void *cls;
51
52   /**
53    * FIXME: document.
54    */
55   char *service_name;
56
57   /**
58    * FIXME: document.
59    */
60   struct GNUNET_CLIENT_Connection *client;
61
62   /**
63    * FIXME: document.
64    */
65   const struct GNUNET_CONFIGURATION_Handle *cfg;
66
67   /**
68    * FIXME: document.
69    */
70   struct GNUNET_TIME_Absolute timeout;
71
72   /**
73    * FIXME: document.
74    */
75   uint16_t type;
76 };
77
78
79 /**
80  * FIXME: document.
81  */
82 static void
83 arm_service_report (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
84 {
85   struct ArmContext *pos = cls;
86   pid_t pid;
87   char *binary;
88   char *config;
89
90   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE))
91     {
92       if (pos->callback != NULL)
93         pos->callback (pos->cls, GNUNET_YES);
94       GNUNET_free (pos);
95       return;
96     }
97   binary = NULL;
98   config = NULL;
99   /* start service */
100   if ((GNUNET_OK !=
101        GNUNET_CONFIGURATION_get_value_filename (pos->cfg,
102                                                 "arm",
103                                                 "BINARY",
104                                                 &binary)) ||
105       (GNUNET_OK !=
106        GNUNET_CONFIGURATION_get_value_filename (pos->cfg,
107                                                 "arm", "CONFIG", &config)))
108     {
109       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
110                   _("Configuration file or binary for ARM not known!\n"));
111       if (pos->callback != NULL)
112         pos->callback (pos->cls, GNUNET_SYSERR);
113       GNUNET_free_non_null (binary);
114       GNUNET_free (pos);
115       return;
116     }
117   pid = GNUNET_OS_start_process (binary, binary, "-d", "-c", config,
118 #if DEBUG_ARM
119                                  "-L", "DEBUG",
120 #endif
121                                  NULL);
122   GNUNET_free (binary);
123   GNUNET_free (config);
124   if (pid == -1)
125     {
126       if (pos->callback != NULL)
127         pos->callback (pos->cls, GNUNET_SYSERR);
128       GNUNET_free (pos);
129       return;
130     }
131   /* FIXME: consider checking again to see if it worked!? */
132   if (pos->callback != NULL)
133     pos->callback (pos->cls, GNUNET_YES);
134   GNUNET_free (pos);
135 }
136
137
138 static void
139 handle_response (void *cls, const struct GNUNET_MessageHeader *msg)
140 {
141   struct ArmContext *sc = cls;
142   int ret;
143
144   if (msg == NULL)
145     {
146       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
147                   _("Error receiving response from ARM service\n"));
148       GNUNET_CLIENT_disconnect (sc->client);
149       if (sc->callback != NULL)
150         sc->callback (sc->cls, GNUNET_SYSERR);
151       GNUNET_free (sc);
152       return;
153     }
154 #if DEBUG_ARM
155   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
156               _("Received response from ARM service\n"));
157 #endif
158   switch (ntohs (msg->type))
159     {
160     case GNUNET_MESSAGE_TYPE_ARM_IS_UP:
161       ret = GNUNET_YES;
162       break;
163     case GNUNET_MESSAGE_TYPE_ARM_IS_DOWN:
164       ret = GNUNET_NO;
165       break;
166     case GNUNET_MESSAGE_TYPE_ARM_IS_UNKNOWN:
167       ret = GNUNET_SYSERR;
168       break;
169     default:
170       GNUNET_break (0);
171       ret = GNUNET_SYSERR;
172     }
173   GNUNET_CLIENT_disconnect (sc->client);
174   if (sc->callback != NULL)
175     sc->callback (sc->cls, ret);
176   GNUNET_free (sc);
177 }
178
179
180 static size_t
181 send_service_msg (void *cls, size_t size, void *buf)
182 {
183   struct ArmContext *sctx = cls;
184   struct GNUNET_MessageHeader *msg;
185   size_t slen;
186
187   if (buf == NULL)
188     {
189       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
190                   _("Error while trying to transmit to ARM service\n"));
191       GNUNET_CLIENT_disconnect (sctx->client);
192       if (sctx->callback != NULL)
193         sctx->callback (sctx->cls, GNUNET_SYSERR);
194       GNUNET_free (sctx->service_name);
195       GNUNET_free (sctx);
196       return 0;
197     }
198 #if DEBUG_ARM
199   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
200               _("Transmitting service request to ARM.\n"));
201 #endif
202   slen = strlen (sctx->service_name) + 1;
203   GNUNET_assert (size >= slen);
204   msg = buf;
205   msg->size = htons (sizeof (struct GNUNET_MessageHeader) + slen);
206   msg->type = htons (sctx->type);
207   memcpy (&msg[1], sctx->service_name, slen);
208   GNUNET_free (sctx->service_name);
209   sctx->service_name = NULL;
210   GNUNET_CLIENT_receive (sctx->client,
211                          &handle_response,
212                          sctx,
213                          GNUNET_TIME_absolute_get_remaining (sctx->timeout));
214   return slen + sizeof (struct GNUNET_MessageHeader);
215 }
216
217
218 /**
219  * Start or stop a service.
220  *
221  * @param service_name name of the service
222  * @param cfg configuration to use (needed to contact ARM;
223  *        the ARM service may internally use a different
224  *        configuration to determine how to start the service).
225  * @param sched scheduler to use
226  * @param timeout how long to wait before failing for good
227  * @param cb callback to invoke when service is ready
228  * @param cb_cls closure for callback
229  * @param type type of the request 
230  */
231 static void
232 change_service (const char *service_name,
233                 const struct GNUNET_CONFIGURATION_Handle *cfg,
234                 struct GNUNET_SCHEDULER_Handle *sched,
235                 struct GNUNET_TIME_Relative timeout,
236                 GNUNET_ARM_Callback cb, void *cb_cls, uint16_t type)
237 {
238   struct GNUNET_CLIENT_Connection *client;
239   struct ArmContext *sctx;
240   size_t slen;
241
242   slen = strlen (service_name) + 1;
243   if (slen + sizeof (struct GNUNET_MessageHeader) >
244       GNUNET_SERVER_MAX_MESSAGE_SIZE)
245     {
246       GNUNET_break (0);
247       if (cb != NULL)
248         cb (cb_cls, GNUNET_NO);
249       return;
250     }
251   client = GNUNET_CLIENT_connect (sched, "arm", cfg);
252   if (client == NULL)
253     {
254       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
255                   _("Failed to connect to ARM service\n"));
256       if (cb != NULL)
257         cb (cb_cls, GNUNET_SYSERR);
258       return;
259     }
260 #if DEBUG_ARM
261   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
262               _("ARM requests starting of service `%s'.\n"), service_name);
263 #endif
264   sctx = GNUNET_malloc (sizeof (struct ArmContext));
265   sctx->callback = cb;
266   sctx->cls = cb_cls;
267   sctx->client = client;
268   sctx->service_name = GNUNET_strdup (service_name);
269   sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
270   sctx->type = type;
271   if (NULL ==
272       GNUNET_CLIENT_notify_transmit_ready (client,
273                                            slen +
274                                            sizeof (struct
275                                                    GNUNET_MessageHeader),
276                                            timeout, &send_service_msg, sctx))
277     {
278       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
279                   _("Failed to transmit request to ARM service\n"));
280       GNUNET_free (sctx->service_name);
281       GNUNET_free (sctx);
282       if (cb != NULL)
283         cb (cb_cls, GNUNET_SYSERR);
284       GNUNET_CLIENT_disconnect (client);
285       return;
286     }
287 }
288
289
290 /**
291  * Start a service.
292  *
293  * @param service_name name of the service
294  * @param cfg configuration to use (needed to contact ARM;
295  *        the ARM service may internally use a different
296  *        configuration to determine how to start the service).
297  * @param sched scheduler to use
298  * @param timeout how long to wait before failing for good
299  * @param cb callback to invoke when service is ready
300  * @param cb_cls closure for callback
301  */
302 void
303 GNUNET_ARM_start_service (const char *service_name,
304                           const struct GNUNET_CONFIGURATION_Handle *cfg,
305                           struct GNUNET_SCHEDULER_Handle *sched,
306                           struct GNUNET_TIME_Relative timeout,
307                           GNUNET_ARM_Callback cb, void *cb_cls)
308 {
309   struct ArmContext *sctx;
310
311   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
312               _("Starting service `%s'\n"), service_name);
313   if (0 == strcmp ("arm", service_name))
314     {
315       sctx = GNUNET_malloc (sizeof (struct ArmContext));
316       sctx->callback = cb;
317       sctx->cls = cb_cls;
318       sctx->cfg = cfg;
319       GNUNET_CLIENT_service_test (sched,
320                                   "arm",
321                                   cfg, timeout, &arm_service_report, sctx);
322       return;
323     }
324   change_service (service_name,
325                   cfg,
326                   sched, timeout, cb, cb_cls, GNUNET_MESSAGE_TYPE_ARM_START);
327 }
328
329
330
331
332 /**
333  * Stop a service.
334  *
335  * @param service_name name of the service
336  * @param cfg configuration to use (needed to contact ARM;
337  *        the ARM service may internally use a different
338  *        configuration to determine how to start the service).
339  * @param sched scheduler to use
340  * @param timeout how long to wait before failing for good
341  * @param cb callback to invoke when service is ready
342  * @param cb_cls closure for callback
343  */
344 void
345 GNUNET_ARM_stop_service (const char *service_name,
346                          const struct GNUNET_CONFIGURATION_Handle *cfg,
347                          struct GNUNET_SCHEDULER_Handle *sched,
348                          struct GNUNET_TIME_Relative timeout,
349                          GNUNET_ARM_Callback cb, void *cb_cls)
350 {
351   struct GNUNET_CLIENT_Connection *client;
352
353   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
354               _("Stopping service `%s'\n"), service_name);
355   if (0 == strcmp ("arm", service_name))
356     {
357       client = GNUNET_CLIENT_connect (sched, "arm", cfg);
358       if (client == NULL)
359         {
360           if (cb != NULL)
361             cb (cb_cls, GNUNET_SYSERR);
362           return;
363         }
364       GNUNET_CLIENT_service_shutdown (client);
365       GNUNET_CLIENT_disconnect (client);
366       if (cb != NULL)
367         cb (cb_cls, GNUNET_NO);
368       return;
369     }
370   change_service (service_name,
371                   cfg,
372                   sched, timeout, cb, cb_cls, GNUNET_MESSAGE_TYPE_ARM_STOP);
373 }
374
375 /* end of arm_api.c */