improving script
[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 struct ArmContext
37 {
38   GNUNET_ARM_Callback callback;
39   void *cls;
40   char *service_name;
41   struct GNUNET_CLIENT_Connection *client;
42   struct GNUNET_CONFIGURATION_Handle *cfg;
43   struct GNUNET_TIME_Absolute timeout;
44   uint16_t type;
45 };
46
47
48 static void
49 arm_service_report (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
50 {
51   struct ArmContext *pos = cls;
52   pid_t pid;
53   char *binary;
54   char *config;
55
56   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE))
57     {
58       if (pos->callback != NULL)
59         pos->callback (pos->cls, GNUNET_YES);
60       GNUNET_free (pos);
61       return;
62     }
63   binary = NULL;
64   config = NULL;
65   /* start service */
66   if ((GNUNET_OK !=
67        GNUNET_CONFIGURATION_get_value_filename (pos->cfg,
68                                                 "arm",
69                                                 "BINARY",
70                                                 &binary)) ||
71       (GNUNET_OK !=
72        GNUNET_CONFIGURATION_get_value_filename (pos->cfg,
73                                                 "arm", "CONFIG", &config)))
74     {
75       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
76                   _("Configuration file or binary for ARM not known!\n"));
77       if (pos->callback != NULL)
78         pos->callback (pos->cls, GNUNET_SYSERR);
79       GNUNET_free_non_null (binary);
80       GNUNET_free (pos);
81       return;
82     }
83   pid = GNUNET_OS_start_process (binary, binary, "-d", "-c", config,
84 #if DEBUG_ARM
85                                  "-L", "DEBUG",
86 #endif
87                                  NULL);
88   GNUNET_free (binary);
89   GNUNET_free (config);
90   if (pid == -1)
91     {
92       if (pos->callback != NULL)
93         pos->callback (pos->cls, GNUNET_SYSERR);
94       GNUNET_free (pos);
95       return;
96     }
97   /* FIXME: consider checking again to see if it worked!? */
98   if (pos->callback != NULL)
99     pos->callback (pos->cls, GNUNET_YES);
100   GNUNET_free (pos);
101 }
102
103
104 static void
105 handle_response (void *cls, const struct GNUNET_MessageHeader *msg)
106 {
107   struct ArmContext *sc = cls;
108   int ret;
109
110   if (msg == NULL)
111     {
112       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
113                   _("Error receiving response from ARM service\n"));
114       GNUNET_CLIENT_disconnect (sc->client);
115       if (sc->callback != NULL)
116         sc->callback (sc->cls, GNUNET_SYSERR);
117       GNUNET_free (sc);
118       return;
119     }
120 #if DEBUG_ARM
121   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
122               _("Received response from ARM service\n"));
123 #endif
124   switch (ntohs (msg->type))
125     {
126     case GNUNET_MESSAGE_TYPE_ARM_IS_UP:
127       ret = GNUNET_YES;
128       break;
129     case GNUNET_MESSAGE_TYPE_ARM_IS_DOWN:
130       ret = GNUNET_NO;
131       break;
132     default:
133       GNUNET_break (0);
134       ret = GNUNET_SYSERR;
135     }
136   GNUNET_CLIENT_disconnect (sc->client);
137   if (sc->callback != NULL)
138     sc->callback (sc->cls, ret);
139   GNUNET_free (sc);
140 }
141
142
143 static size_t
144 send_service_msg (void *cls, size_t size, void *buf)
145 {
146   struct ArmContext *sctx = cls;
147   struct GNUNET_MessageHeader *msg;
148   size_t slen;
149
150   if (buf == NULL)
151     {
152       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
153                   _("Error while trying to transmit to ARM service\n"));
154       GNUNET_CLIENT_disconnect (sctx->client);
155       if (sctx->callback != NULL)
156         sctx->callback (sctx->cls, GNUNET_SYSERR);
157       GNUNET_free (sctx->service_name);
158       GNUNET_free (sctx);
159       return 0;
160     }
161 #if DEBUG_ARM
162   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
163               _("Transmitting service request to ARM.\n"));
164 #endif
165   slen = strlen (sctx->service_name) + 1;
166   GNUNET_assert (size >= slen);
167   msg = buf;
168   msg->size = htons (sizeof (struct GNUNET_MessageHeader) + slen);
169   msg->type = htons (sctx->type);
170   memcpy (&msg[1], sctx->service_name, slen);
171   GNUNET_free (sctx->service_name);
172   sctx->service_name = NULL;
173   GNUNET_CLIENT_receive (sctx->client,
174                          &handle_response,
175                          sctx,
176                          GNUNET_TIME_absolute_get_remaining (sctx->timeout));
177   return slen + sizeof (struct GNUNET_MessageHeader);
178 }
179
180
181 /**
182  * Start or stop a service.
183  *
184  * @param service_name name of the service
185  * @param cfg configuration to use (needed to contact ARM;
186  *        the ARM service may internally use a different
187  *        configuration to determine how to start the service).
188  * @param sched scheduler to use
189  * @param timeout how long to wait before failing for good
190  * @param cb callback to invoke when service is ready
191  * @param cb_cls closure for callback
192  */
193 static void
194 change_service (const char *service_name,
195                 struct GNUNET_CONFIGURATION_Handle *cfg,
196                 struct GNUNET_SCHEDULER_Handle *sched,
197                 struct GNUNET_TIME_Relative timeout,
198                 GNUNET_ARM_Callback cb, void *cb_cls, uint16_t type)
199 {
200   struct GNUNET_CLIENT_Connection *client;
201   struct ArmContext *sctx;
202   size_t slen;
203
204   slen = strlen (service_name) + 1;
205   if (slen + sizeof (struct GNUNET_MessageHeader) >
206       GNUNET_SERVER_MAX_MESSAGE_SIZE)
207     {
208       GNUNET_break (0);
209       if (cb != NULL)
210         cb (cb_cls, GNUNET_NO);
211       return;
212     }
213   client = GNUNET_CLIENT_connect (sched, "arm", cfg);
214   if (client == NULL)
215     {
216       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
217                   _("Failed to connect to ARM service\n"));
218       if (cb != NULL)
219         cb (cb_cls, GNUNET_SYSERR);
220       return;
221     }
222 #if DEBUG_ARM
223   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
224               _("ARM requests starting of service `%s'.\n"), service_name);
225 #endif
226   sctx = GNUNET_malloc (sizeof (struct ArmContext));
227   sctx->callback = cb;
228   sctx->cls = cb_cls;
229   sctx->client = client;
230   sctx->service_name = GNUNET_strdup (service_name);
231   sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
232   sctx->type = type;
233   if (NULL ==
234       GNUNET_CLIENT_notify_transmit_ready (client,
235                                            slen +
236                                            sizeof (struct
237                                                    GNUNET_MessageHeader),
238                                            timeout, &send_service_msg, sctx))
239     {
240       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
241                   _("Failed to transmit request to ARM service\n"));
242       GNUNET_free (sctx->service_name);
243       GNUNET_free (sctx);
244       if (cb != NULL)
245         cb (cb_cls, GNUNET_SYSERR);
246       GNUNET_CLIENT_disconnect (client);
247       return;
248     }
249 }
250
251
252 /**
253  * Start a service.
254  *
255  * @param service_name name of the service
256  * @param cfg configuration to use (needed to contact ARM;
257  *        the ARM service may internally use a different
258  *        configuration to determine how to start the service).
259  * @param sched scheduler to use
260  * @param timeout how long to wait before failing for good
261  * @param cb callback to invoke when service is ready
262  * @param cb_cls closure for callback
263  */
264 void
265 GNUNET_ARM_start_service (const char *service_name,
266                           struct GNUNET_CONFIGURATION_Handle *cfg,
267                           struct GNUNET_SCHEDULER_Handle *sched,
268                           struct GNUNET_TIME_Relative timeout,
269                           GNUNET_ARM_Callback cb, void *cb_cls)
270 {
271   struct ArmContext *sctx;
272
273   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
274               _("Starting service `%s'\n"), service_name);
275   if (0 == strcmp ("arm", service_name))
276     {
277       sctx = GNUNET_malloc (sizeof (struct ArmContext));
278       sctx->callback = cb;
279       sctx->cls = cb_cls;
280       sctx->cfg = cfg;
281       GNUNET_CLIENT_service_test (sched,
282                                   "arm",
283                                   cfg, timeout, &arm_service_report, sctx);
284       return;
285     }
286   change_service (service_name,
287                   cfg,
288                   sched, timeout, cb, cb_cls, GNUNET_MESSAGE_TYPE_ARM_START);
289 }
290
291
292
293
294 /**
295  * Stop a service.
296  *
297  * @param service_name name of the service
298  * @param cfg configuration to use (needed to contact ARM;
299  *        the ARM service may internally use a different
300  *        configuration to determine how to start the service).
301  * @param sched scheduler to use
302  * @param timeout how long to wait before failing for good
303  * @param cb callback to invoke when service is ready
304  * @param cb_cls closure for callback
305  */
306 void
307 GNUNET_ARM_stop_service (const char *service_name,
308                          struct GNUNET_CONFIGURATION_Handle *cfg,
309                          struct GNUNET_SCHEDULER_Handle *sched,
310                          struct GNUNET_TIME_Relative timeout,
311                          GNUNET_ARM_Callback cb, void *cb_cls)
312 {
313   struct GNUNET_CLIENT_Connection *client;
314
315   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
316               _("Stopping service `%s'\n"), service_name);
317   if (0 == strcmp ("arm", service_name))
318     {
319       client = GNUNET_CLIENT_connect (sched, "arm", cfg);
320       if (client == NULL)
321         {
322           if (cb != NULL)
323             cb (cb_cls, GNUNET_SYSERR);
324           return;
325         }
326       GNUNET_CLIENT_service_shutdown (client);
327       GNUNET_CLIENT_disconnect (client);
328       if (cb != NULL)
329         cb (cb_cls, GNUNET_NO);
330       return;
331     }
332   change_service (service_name,
333                   cfg,
334                   sched, timeout, cb, cb_cls, GNUNET_MESSAGE_TYPE_ARM_STOP);
335 }
336
337 /* end of arm_api.c */