working version
[oweals/gnunet.git] / src / arm / arm_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010 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 3, 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  * Handle for interacting with ARM.
38  */
39 struct GNUNET_ARM_Handle
40 {
41
42   /**
43    * Our connection to the ARM service.
44    */
45   struct GNUNET_CLIENT_Connection *client;
46
47   /**
48    * The configuration that we are using.
49    */
50   struct GNUNET_CONFIGURATION_Handle *cfg;
51
52 };
53
54
55 /**
56  * Context for handling the shutdown of a service.
57  */
58 struct ShutdownContext
59 {
60   /**
61    * Connection to the service that is being shutdown.
62    */
63   struct GNUNET_CLIENT_Connection *sock;
64
65   /**
66    * Time allowed for shutdown to happen.
67    */
68   struct GNUNET_TIME_Absolute timeout;
69
70   /**
71    * Task set up to cancel the shutdown request on timeout.
72    */
73   GNUNET_SCHEDULER_TaskIdentifier cancel_task;
74
75   /**
76    * Task to call once shutdown complete
77    */
78   GNUNET_CLIENT_ShutdownTask cont;
79
80   /**
81    * Closure for shutdown continuation
82    */
83   void *cont_cls;
84
85   /**
86    * We received a confirmation that the service will shut down.
87    */
88   int confirmed;
89
90 };
91
92 /**
93  * Handler receiving response to service shutdown requests.
94  * First call with NULL: service misbehaving, or something.
95  * First call with GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN_ACK:
96  *   - service will shutdown
97  * Second call with NULL:
98  *   - service has now really shut down.
99  *
100  * @param cls closure
101  * @param msg NULL, indicating socket closure.
102  */
103 static void
104 service_shutdown_handler (void *cls, const struct GNUNET_MessageHeader *msg)
105 {
106   struct ShutdownContext *shutdown_ctx = cls;
107
108   if ((msg == NULL) && (shutdown_ctx->confirmed != GNUNET_YES))
109   {
110 #if DEBUG_ARM
111     /* Means the other side closed the connection and never confirmed a shutdown */
112     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
113                 "Service handle shutdown before ACK!\n");
114 #endif
115     if (shutdown_ctx->cont != NULL)
116       shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_SYSERR);
117     GNUNET_SCHEDULER_cancel (shutdown_ctx->cancel_task);
118     GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
119     GNUNET_free (shutdown_ctx);
120   }
121   else if ((msg == NULL) && (shutdown_ctx->confirmed == GNUNET_YES))
122   {
123 #if DEBUG_ARM
124     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service shutdown complete.\n");
125 #endif
126     if (shutdown_ctx->cont != NULL)
127       shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_NO);
128
129     GNUNET_SCHEDULER_cancel (shutdown_ctx->cancel_task);
130     GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
131     GNUNET_free (shutdown_ctx);
132   }
133   else
134   {
135     GNUNET_assert (ntohs (msg->size) == sizeof (struct GNUNET_MessageHeader));
136     switch (ntohs (msg->type))
137     {
138     case GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN_ACK:
139 #if DEBUG_ARM
140       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
141                   "Received confirmation for service shutdown.\n");
142 #endif
143       shutdown_ctx->confirmed = GNUNET_YES;
144       GNUNET_CLIENT_receive (shutdown_ctx->sock, &service_shutdown_handler,
145                              shutdown_ctx, GNUNET_TIME_UNIT_FOREVER_REL);
146       break;
147     default:                   /* Fall through */
148 #if DEBUG_ARM
149       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service shutdown refused!\n");
150 #endif
151       if (shutdown_ctx->cont != NULL)
152         shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_YES);
153
154       GNUNET_SCHEDULER_cancel (shutdown_ctx->cancel_task);
155       GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
156       GNUNET_free (shutdown_ctx);
157       break;
158     }
159   }
160 }
161
162 /**
163  * Shutting down took too long, cancel receive and return error.
164  *
165  * @param cls closure
166  * @param tc context information (why was this task triggered now)
167  */
168 void
169 service_shutdown_cancel (void *cls,
170                          const struct GNUNET_SCHEDULER_TaskContext *tc)
171 {
172   struct ShutdownContext *shutdown_ctx = cls;
173
174 #if DEBUG_ARM
175   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "service_shutdown_cancel called!\n");
176 #endif
177   shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_SYSERR);
178   GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
179   GNUNET_free (shutdown_ctx);
180 }
181
182
183 /**
184  * If possible, write a shutdown message to the target
185  * buffer and destroy the client connection.
186  *
187  * @param cls the "struct GNUNET_CLIENT_Connection" to destroy
188  * @param size number of bytes available in buf
189  * @param buf NULL on error, otherwise target buffer
190  * @return number of bytes written to buf
191  */
192 static size_t
193 write_shutdown (void *cls, size_t size, void *buf)
194 {
195   struct GNUNET_MessageHeader *msg;
196   struct ShutdownContext *shutdown_ctx = cls;
197
198   if (size < sizeof (struct GNUNET_MessageHeader))
199   {
200     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
201                 _("Failed to transmit shutdown request to client.\n"));
202     shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_SYSERR);
203     GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
204     GNUNET_free (shutdown_ctx);
205     return 0;                   /* client disconnected */
206   }
207
208   GNUNET_CLIENT_receive (shutdown_ctx->sock, &service_shutdown_handler,
209                          shutdown_ctx, GNUNET_TIME_UNIT_FOREVER_REL);
210   shutdown_ctx->cancel_task =
211       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
212                                     (shutdown_ctx->timeout),
213                                     &service_shutdown_cancel, shutdown_ctx);
214   msg = (struct GNUNET_MessageHeader *) buf;
215   msg->type = htons (GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN);
216   msg->size = htons (sizeof (struct GNUNET_MessageHeader));
217   return sizeof (struct GNUNET_MessageHeader);
218 }
219
220
221 /**
222  * Request that the service should shutdown.
223  * Afterwards, the connection will automatically be
224  * disconnected.  Hence the "sock" should not
225  * be used by the caller after this call
226  * (calling this function frees "sock" after a while).
227  *
228  * @param sock the socket connected to the service
229  * @param timeout how long to wait before giving up on transmission
230  * @param cont continuation to call once the service is really down
231  * @param cont_cls closure for continuation
232  *
233  */
234 static void
235 arm_service_shutdown (struct GNUNET_CLIENT_Connection *sock,
236                       struct GNUNET_TIME_Relative timeout,
237                       GNUNET_CLIENT_ShutdownTask cont, void *cont_cls)
238 {
239   struct ShutdownContext *shutdown_ctx;
240
241   shutdown_ctx = GNUNET_malloc (sizeof (struct ShutdownContext));
242   shutdown_ctx->cont = cont;
243   shutdown_ctx->cont_cls = cont_cls;
244   shutdown_ctx->sock = sock;
245   shutdown_ctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
246   GNUNET_CLIENT_notify_transmit_ready (sock,
247                                        sizeof (struct GNUNET_MessageHeader),
248                                        timeout, GNUNET_YES, &write_shutdown,
249                                        shutdown_ctx);
250 }
251
252
253 /**
254  * Setup a context for communicating with ARM.  Note that this
255  * can be done even if the ARM service is not yet running.
256  *
257  * @param cfg configuration to use (needed to contact ARM;
258  *        the ARM service may internally use a different
259  *        configuration to determine how to start the service).
260  * @param service service that *this* process is implementing/providing, can be NULL
261  * @return context to use for further ARM operations, NULL on error
262  */
263 struct GNUNET_ARM_Handle *
264 GNUNET_ARM_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
265                     const char *service)
266 {
267   struct GNUNET_ARM_Handle *ret;
268
269   ret = GNUNET_malloc (sizeof (struct GNUNET_ARM_Handle));
270   ret->cfg = GNUNET_CONFIGURATION_dup (cfg);
271   return ret;
272 }
273
274
275 /**
276  * Disconnect from the ARM service.
277  *
278  * @param h the handle that was being used
279  */
280 void
281 GNUNET_ARM_disconnect (struct GNUNET_ARM_Handle *h)
282 {
283   if (h->client != NULL)
284     GNUNET_CLIENT_disconnect (h->client, GNUNET_NO);
285   GNUNET_CONFIGURATION_destroy (h->cfg);
286   GNUNET_free (h);
287 }
288
289 struct ARM_ShutdownContext
290 {
291   /**
292    * Callback to call once shutdown complete.
293    */
294   GNUNET_ARM_Callback cb;
295
296   /**
297    * Closure for callback.
298    */
299   void *cb_cls;
300 };
301
302
303
304 /**
305  * Internal state for a request with ARM.
306  */
307 struct RequestContext
308 {
309
310   /**
311    * Pointer to our handle with ARM.
312    */
313   struct GNUNET_ARM_Handle *h;
314
315   /**
316    * Function to call with a status code for the requested operation.
317    */
318   GNUNET_ARM_Callback callback;
319
320   /**
321    * Closure for "callback".
322    */
323   void *cls;
324
325   /**
326    * Timeout for the operation.
327    */
328   struct GNUNET_TIME_Absolute timeout;
329
330   /**
331    * Type of the request expressed as a message type (start or stop).
332    */
333   uint16_t type;
334
335 };
336
337 #include "do_start_process.c"
338
339
340 /**
341  * A client specifically requested starting of ARM itself.
342  * This function is called with information about whether
343  * or not ARM is running; if it is, report success.  If
344  * it is not, start the ARM process.
345  *
346  * @param cls the context for the request that we will report on (struct RequestContext*)
347  * @param tc why were we called (reason says if ARM is running)
348  */
349 static void
350 arm_service_report (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
351 {
352   struct RequestContext *pos = cls;
353   struct GNUNET_OS_Process *proc;
354   char *binary;
355   char *config;
356   char *loprefix;
357   char *lopostfix;
358
359   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE))
360   {
361 #if DEBUG_ARM
362     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
363                 "Looks like `%s' is already running.\n", "gnunet-service-arm");
364 #endif
365     /* arm is running! */
366     if (pos->callback != NULL)
367       pos->callback (pos->cls, GNUNET_YES);
368     GNUNET_free (pos);
369     return;
370   }
371 #if DEBUG_ARM
372   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
373               "Looks like `%s' is not running, will start it.\n",
374               "gnunet-service-arm");
375 #endif
376   if (GNUNET_OK !=
377       GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, "arm", "PREFIX",
378                                              &loprefix))
379     loprefix = GNUNET_strdup ("");
380   if (GNUNET_OK !=
381       GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, "arm", "OPTIONS",
382                                              &lopostfix))
383     lopostfix = GNUNET_strdup ("");
384   if (GNUNET_OK !=
385       GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, "arm", "BINARY",
386                                              &binary))
387   {
388     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
389                 _
390                 ("Configuration failes to specify option `%s' in section `%s'!\n"),
391                 "BINARY", "arm");
392     if (pos->callback != NULL)
393       pos->callback (pos->cls, GNUNET_SYSERR);
394     GNUNET_free (pos);
395     GNUNET_free (loprefix);
396     GNUNET_free (lopostfix);
397     return;
398   }
399   if (GNUNET_OK !=
400       GNUNET_CONFIGURATION_get_value_filename (pos->h->cfg, "arm", "CONFIG",
401                                                &config))
402   {
403     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
404                 _
405                 ("Configuration fails to specify option `%s' in section `%s'!\n"),
406                 "CONFIG", "arm");
407     if (pos->callback != NULL)
408       pos->callback (pos->cls, GNUNET_SYSERR);
409     GNUNET_free (binary);
410     GNUNET_free (pos);
411     GNUNET_free (loprefix);
412     GNUNET_free (lopostfix);
413     return;
414   }
415   if ((GNUNET_YES == GNUNET_CONFIGURATION_have_value (pos->h->cfg, "TESTING", "WEAKRANDOM")) && (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (pos->h->cfg, "TESTING", "WEAKRANDOM")) && (GNUNET_NO == GNUNET_CONFIGURATION_have_value (pos->h->cfg, "TESTING", "HOSTFILE"))      /* Means we are ONLY running locally */
416       )
417   {
418     /* we're clearly running a test, don't daemonize */
419     proc = do_start_process (NULL, loprefix, binary, "-c", config,
420 #if DEBUG_ARM
421                              "-L", "DEBUG",
422 #endif
423                              /* no daemonization! */
424                              lopostfix, NULL);
425   }
426   else
427   {
428     proc = do_start_process (NULL, loprefix, binary, "-c", config,
429 #if DEBUG_ARM
430                              "-L", "DEBUG",
431 #endif
432                              "-d", lopostfix, NULL);
433   }
434   GNUNET_free (binary);
435   GNUNET_free (config);
436   GNUNET_free (loprefix);
437   GNUNET_free (lopostfix);
438   if (proc == NULL)
439   {
440     if (pos->callback != NULL)
441       pos->callback (pos->cls, GNUNET_SYSERR);
442     GNUNET_free (pos);
443     return;
444   }
445   if (pos->callback != NULL)
446     pos->callback (pos->cls, GNUNET_YES);
447   GNUNET_free (proc);
448   GNUNET_free (pos);
449 }
450
451
452 /**
453  * Process a response from ARM to a request for a change in service
454  * status.
455  *
456  * @param cls the request context
457  * @param msg the response
458  */
459 static void
460 handle_response (void *cls, const struct GNUNET_MessageHeader *msg)
461 {
462   struct RequestContext *sc = cls;
463   int ret;
464
465   if (msg == NULL)
466   {
467     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
468                 _
469                 ("Error receiving response to `%s' request from ARM for service `%s'\n"),
470                 (sc->type == GNUNET_MESSAGE_TYPE_ARM_START) ? "START" : "STOP",
471                 (const char *) &sc[1]);
472     GNUNET_CLIENT_disconnect (sc->h->client, GNUNET_NO);
473     sc->h->client = GNUNET_CLIENT_connect ("arm", sc->h->cfg);
474     GNUNET_assert (NULL != sc->h->client);
475     GNUNET_CLIENT_ignore_shutdown (sc->h->client, GNUNET_YES);
476     if (sc->callback != NULL)
477       sc->callback (sc->cls, GNUNET_SYSERR);
478     GNUNET_free (sc);
479     return;
480   }
481 #if DEBUG_ARM
482   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
483               "Received response from ARM for service `%s': %u\n",
484               (const char *) &sc[1], ntohs (msg->type));
485 #endif
486   switch (ntohs (msg->type))
487   {
488   case GNUNET_MESSAGE_TYPE_ARM_IS_UP:
489     ret = GNUNET_YES;
490     break;
491   case GNUNET_MESSAGE_TYPE_ARM_IS_DOWN:
492     ret = GNUNET_NO;
493     break;
494   case GNUNET_MESSAGE_TYPE_ARM_IS_UNKNOWN:
495     ret = GNUNET_SYSERR;
496     break;
497   default:
498     GNUNET_break (0);
499     ret = GNUNET_SYSERR;
500   }
501   if (sc->callback != NULL)
502     sc->callback (sc->cls, ret);
503   GNUNET_free (sc);
504 }
505
506
507 /**
508  * Start or stop a service.
509  *
510  * @param h handle to ARM
511  * @param service_name name of the service
512  * @param timeout how long to wait before failing for good
513  * @param cb callback to invoke when service is ready
514  * @param cb_cls closure for callback
515  * @param type type of the request
516  */
517 static void
518 change_service (struct GNUNET_ARM_Handle *h, const char *service_name,
519                 struct GNUNET_TIME_Relative timeout, GNUNET_ARM_Callback cb,
520                 void *cb_cls, uint16_t type)
521 {
522   struct RequestContext *sctx;
523   size_t slen;
524   struct GNUNET_MessageHeader *msg;
525
526   slen = strlen (service_name) + 1;
527   if (slen + sizeof (struct GNUNET_MessageHeader) >=
528       GNUNET_SERVER_MAX_MESSAGE_SIZE)
529   {
530     GNUNET_break (0);
531     if (cb != NULL)
532       cb (cb_cls, GNUNET_NO);
533     return;
534   }
535 #if DEBUG_ARM
536   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
537               (type ==
538                GNUNET_MESSAGE_TYPE_ARM_START) ?
539               _("Requesting start of service `%s'.\n") :
540               _("Requesting termination of service `%s'.\n"), service_name);
541 #endif
542   sctx = GNUNET_malloc (sizeof (struct RequestContext) + slen);
543   sctx->h = h;
544   sctx->callback = cb;
545   sctx->cls = cb_cls;
546   sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
547   sctx->type = type;
548   memcpy (&sctx[1], service_name, slen);
549   msg = GNUNET_malloc (sizeof (struct GNUNET_MessageHeader) + slen);
550   msg->size = htons (sizeof (struct GNUNET_MessageHeader) + slen);
551   msg->type = htons (sctx->type);
552   memcpy (&msg[1], service_name, slen);
553   if (GNUNET_OK !=
554       GNUNET_CLIENT_transmit_and_get_response (sctx->h->client, msg,
555                                                GNUNET_TIME_absolute_get_remaining
556                                                (sctx->timeout), GNUNET_YES,
557                                                &handle_response, sctx))
558   {
559     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
560                 (type ==
561                  GNUNET_MESSAGE_TYPE_ARM_START) ?
562                 _
563                 ("Error while trying to transmit request to start `%s' to ARM\n")
564                 :
565                 _
566                 ("Error while trying to transmit request to stop `%s' to ARM\n"),
567                 (const char *) &service_name);
568     if (cb != NULL)
569       cb (cb_cls, GNUNET_SYSERR);
570     GNUNET_free (sctx);
571     GNUNET_free (msg);
572     return;
573   }
574   GNUNET_free (msg);
575 }
576
577
578 /**
579  * Start a service.
580  *
581  * @param h handle to ARM
582  * @param service_name name of the service
583  * @param timeout how long to wait before failing for good
584  * @param cb callback to invoke when service is ready
585  * @param cb_cls closure for callback
586  */
587 void
588 GNUNET_ARM_start_service (struct GNUNET_ARM_Handle *h, const char *service_name,
589                           struct GNUNET_TIME_Relative timeout,
590                           GNUNET_ARM_Callback cb, void *cb_cls)
591 {
592   struct RequestContext *sctx;
593   struct GNUNET_CLIENT_Connection *client;
594   size_t slen;
595
596 #if DEBUG_ARM
597   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
598               _("Asked to start service `%s' within %llu ms\n"), service_name,
599               (unsigned long long) timeout.rel_value);
600 #endif
601   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
602               _("Asked to start service `%s' within %llu ms\n"), service_name,
603               (unsigned long long) timeout.rel_value);
604   if (0 == strcasecmp ("arm", service_name))
605   {
606     slen = strlen ("arm") + 1;
607     sctx = GNUNET_malloc (sizeof (struct RequestContext) + slen);
608     sctx->h = h;
609     sctx->callback = cb;
610     sctx->cls = cb_cls;
611     sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
612     memcpy (&sctx[1], service_name, slen);
613     GNUNET_CLIENT_service_test ("arm", h->cfg, timeout, &arm_service_report,
614                                 sctx);
615     return;
616   }
617   if (h->client == NULL)
618   {
619     client = GNUNET_CLIENT_connect ("arm", h->cfg);
620     if (client == NULL)
621     {
622       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
623                   "arm_api, GNUNET_CLIENT_connect returned NULL\n");
624       cb (cb_cls, GNUNET_SYSERR);
625       return;
626     }
627     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
628                 "arm_api, GNUNET_CLIENT_connect returned non-NULL\n");
629     GNUNET_CLIENT_ignore_shutdown (client, GNUNET_YES);
630     h->client = client;
631   }
632   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "arm_api, h->client non-NULL\n");
633   change_service (h, service_name, timeout, cb, cb_cls,
634                   GNUNET_MESSAGE_TYPE_ARM_START);
635 }
636
637 /**
638  * Callback from the arm stop service call, indicates that the arm service
639  * is well and truly dead, won't die, or an error occurred.
640  *
641  * @param cls closure for the callback
642  * @param reason reason for callback, GNUNET_NO if arm is shutdown
643  *        GNUNET_YES if arm remains running, and GNUNET_SYSERR on error
644  */
645 void
646 arm_shutdown_callback (void *cls, int reason)
647 {
648   struct ARM_ShutdownContext *arm_shutdown_ctx = cls;
649
650   if (arm_shutdown_ctx->cb != NULL)
651     arm_shutdown_ctx->cb (arm_shutdown_ctx->cb_cls, reason);
652
653   GNUNET_free (arm_shutdown_ctx);
654 }
655
656
657 /**
658  * Stop a service.
659  *
660  * @param h handle to ARM
661  * @param service_name name of the service
662  * @param timeout how long to wait before failing for good
663  * @param cb callback to invoke when service is ready
664  * @param cb_cls closure for callback
665  */
666 void
667 GNUNET_ARM_stop_service (struct GNUNET_ARM_Handle *h, const char *service_name,
668                          struct GNUNET_TIME_Relative timeout,
669                          GNUNET_ARM_Callback cb, void *cb_cls)
670 {
671   struct ARM_ShutdownContext *arm_shutdown_ctx;
672   struct GNUNET_CLIENT_Connection *client;
673
674   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
675               _("Stopping service `%s' within %llu ms\n"), service_name,
676               (unsigned long long) timeout.rel_value);
677   if (h->client == NULL)
678   {
679     client = GNUNET_CLIENT_connect ("arm", h->cfg);
680     if (client == NULL)
681     {
682       cb (cb_cls, GNUNET_SYSERR);
683       return;
684     }
685     GNUNET_CLIENT_ignore_shutdown (client, GNUNET_YES);
686     h->client = client;
687   }
688   if (0 == strcasecmp ("arm", service_name))
689   {
690     arm_shutdown_ctx = GNUNET_malloc (sizeof (struct ARM_ShutdownContext));
691     arm_shutdown_ctx->cb = cb;
692     arm_shutdown_ctx->cb_cls = cb_cls;
693     arm_service_shutdown (h->client, timeout, &arm_shutdown_callback,
694                           arm_shutdown_ctx);
695     h->client = NULL;
696     return;
697   }
698   change_service (h, service_name, timeout, cb, cb_cls,
699                   GNUNET_MESSAGE_TYPE_ARM_STOP);
700 }
701
702
703 /* end of arm_api.c */