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