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