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