small API change: do no longer pass rarely needed GNUNET_SCHEDULER_TaskContext to...
[oweals/gnunet.git] / src / util / client_manager.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2013 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20
21 /**
22  * @file util/client_manager.c
23  * @brief Client manager; higher level client API with transmission queue
24  * and message handler registration.
25  * @author Gabor X Toth
26  */
27
28 #include <inttypes.h>
29
30 #include "platform.h"
31 #include "gnunet_util_lib.h"
32
33 #define LOG(kind,...) GNUNET_log_from (kind, "util-client-mgr", __VA_ARGS__)
34
35
36 struct OperationListItem
37 {
38   struct OperationListItem *prev;
39   struct OperationListItem *next;
40
41   /**
42    * Operation ID.
43    */
44   uint64_t op_id;
45
46   /**
47    * Continuation to invoke with the result of an operation.
48    */
49   GNUNET_ResultCallback result_cb;
50
51   /**
52    * Closure for @a result_cb.
53    */
54   void *cls;
55 };
56
57
58 /**
59  * List of arrays of message handlers.
60  */
61 struct HandlersListItem
62 {
63   struct HandlersListItem *prev;
64   struct HandlersListItem *next;
65
66   /**
67    * NULL-terminated array of handlers.
68    */
69   const struct GNUNET_CLIENT_MANAGER_MessageHandler *handlers;
70 };
71
72
73 struct MessageQueueItem
74 {
75   struct MessageQueueItem *prev;
76   struct MessageQueueItem *next;
77   struct GNUNET_MessageHeader *msg;
78 };
79
80
81 struct GNUNET_CLIENT_MANAGER_Connection
82 {
83   /**
84    * Configuration to use.
85    */
86   const struct GNUNET_CONFIGURATION_Handle *cfg;
87
88   /**
89    * Client connection to service.
90    */
91   struct GNUNET_CLIENT_Connection *client;
92
93   /**
94    * Currently pending transmission request, or NULL for none.
95    */
96   struct GNUNET_CLIENT_TransmitHandle *client_tmit;
97
98   /**
99    * Service name to connect to.
100    */
101   const char *service_name;
102
103   /**
104    * Head of messages to transmit to the service.
105    */
106   struct MessageQueueItem *tmit_head;
107
108   /**
109    * Tail of messages to transmit to the service.
110    */
111   struct MessageQueueItem *tmit_tail;
112
113   /**
114    * Message handlers.
115    */
116   const struct GNUNET_CLIENT_MANAGER_MessageHandler *handlers;
117
118   /**
119    * First operation in the linked list.
120    */
121   struct OperationListItem *op_head;
122
123   /**
124    * Last operation in the linked list.
125    */
126   struct OperationListItem *op_tail;
127
128   /**
129    * Last operation ID used.
130    */
131   uint64_t last_op_id;
132
133   /**
134    * Disconnect callback.
135    */
136   void (*disconnect_cb)(void *);
137
138   /**
139    * Disconnect closure.
140    */
141   void *disconnect_cls;
142
143   /**
144    * User context value.
145    * @see GNUNET_CLIENT_MANAGER_set_user_context()
146    * @see GNUNET_CLIENT_MANAGER_get_user_context()
147    */
148   void *user_ctx;
149
150   /**
151    * Last size given when user context was initialized.
152    * Used for sanity check.
153    */
154   size_t user_ctx_size;
155
156   /**
157    * Task doing exponential back-off trying to reconnect.
158    */
159   struct GNUNET_SCHEDULER_Task * reconnect_task;
160
161   /**
162    * Time for next connect retry.
163    */
164   struct GNUNET_TIME_Relative reconnect_delay;
165
166   /**
167    * Are we currently polling for incoming messages?
168    */
169   uint8_t in_receive;
170
171   /**
172    * #GNUNET_YES if GNUNET_CLIENT_MANAGER_disconnect() was called
173    * and we're transmitting the last messages from the queue.
174    */
175   uint8_t is_disconnecting;
176 };
177
178
179 /**
180  * Handle received messages from the service.
181  */
182 static void
183 recv_message (void *cls, const struct GNUNET_MessageHeader *msg)
184 {
185   struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
186   uint16_t type = 0, size = 0;
187
188   if (NULL != msg)
189   {
190     type = ntohs (msg->type);
191     size = ntohs (msg->size);
192     /* FIXME: decrease reconnect_delay gradually after a successful reconnection */
193   }
194   else /* disconnected */
195   {
196     mgr->client_tmit = NULL;
197   }
198
199   if (GNUNET_YES == mgr->is_disconnecting)
200     return;
201
202   size_t i = 0;
203   while (NULL != mgr->handlers[i].callback)
204   {
205     const struct GNUNET_CLIENT_MANAGER_MessageHandler *mh = &mgr->handlers[i];
206     if ((mh->type == type) || (mh->type == GNUNET_MESSAGE_TYPE_ALL))
207     {
208       if (0 != mh->expected_size
209           && ((GNUNET_NO == mh->is_variable_size && size != mh->expected_size)
210               || (GNUNET_YES == mh->is_variable_size && size < mh->expected_size)))
211       {
212         LOG (GNUNET_ERROR_TYPE_ERROR,
213              "Expected %u bytes for message of type %u, got %u.\n",
214              mh->expected_size, type, size);
215         GNUNET_break_op (0);
216         GNUNET_CLIENT_disconnect (mgr->client);
217         mgr->client = NULL;
218         recv_message (mgr, NULL);
219         break;
220       }
221       mh->callback (mh->callback_cls, mgr, msg);
222     }
223     i++;
224   }
225   if (NULL != mgr->client)
226   {
227     GNUNET_CLIENT_receive (mgr->client, &recv_message, mgr,
228                            GNUNET_TIME_UNIT_FOREVER_REL);
229   }
230 }
231
232
233 /**
234  * Schedule transmission of the next message from our queue.
235  *
236  * @param mgr  Client manager connection.
237  */
238 static void
239 transmit_next (struct GNUNET_CLIENT_MANAGER_Connection *mgr);
240
241
242 static void
243 schedule_disconnect (void *cls)
244 {
245   struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
246   GNUNET_CLIENT_MANAGER_disconnect (mgr, GNUNET_NO,
247                                     mgr->disconnect_cb, mgr->disconnect_cls);
248 }
249
250
251 /**
252  * Transmit next message to service.
253  *
254  * @param cls
255  *        struct GNUNET_CLIENT_MANAGER_Connection
256  * @param buf_size
257  *        Number of bytes available in @a buf.
258  * @param buf
259  *        Where to copy the message.
260  *
261  * @return Number of bytes copied to @a buf.
262  */
263 static size_t
264 send_next_message (void *cls, size_t buf_size, void *buf)
265 {
266   LOG (GNUNET_ERROR_TYPE_DEBUG, "send_next_message()\n");
267   struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
268
269   if (NULL == buf)
270   {
271     /* disconnected */
272     recv_message (mgr, NULL);
273     return 0;
274   }
275
276   struct MessageQueueItem *mqi = mgr->tmit_head;
277   if (NULL == mqi)
278     return 0;
279
280   uint16_t size = ntohs (mqi->msg->size);
281   mgr->client_tmit = NULL;
282   GNUNET_assert (size <= buf_size);
283   memcpy (buf, mqi->msg, size);
284
285   GNUNET_CONTAINER_DLL_remove (mgr->tmit_head, mgr->tmit_tail, mqi);
286   GNUNET_free (mqi->msg);
287   GNUNET_free (mqi);
288
289   if (NULL != mgr->tmit_head)
290   {
291     transmit_next (mgr);
292   }
293   else if (GNUNET_YES == mgr->is_disconnecting)
294   {
295     GNUNET_SCHEDULER_add_now (&schedule_disconnect, mgr);
296     return size;
297   }
298
299   if (GNUNET_NO == mgr->in_receive)
300   {
301     mgr->in_receive = GNUNET_YES;
302     GNUNET_CLIENT_receive (mgr->client, &recv_message, mgr,
303                            GNUNET_TIME_UNIT_FOREVER_REL);
304   }
305   return size;
306 }
307
308
309 /**
310  * Schedule transmission of the next message from our queue.
311  *
312  * @param mgr  Client manager connection.
313  */
314 static void
315 transmit_next (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
316 {
317   LOG (GNUNET_ERROR_TYPE_DEBUG, "transmit_next()\n");
318   if (NULL != mgr->client_tmit || NULL == mgr->client)
319     return;
320
321   if (NULL == mgr->tmit_head)
322   {
323     if (GNUNET_YES == mgr->is_disconnecting)
324       GNUNET_CLIENT_MANAGER_disconnect (mgr, GNUNET_NO,
325                                         mgr->disconnect_cb, mgr->disconnect_cls);
326     return;
327   }
328
329   mgr->client_tmit
330     = GNUNET_CLIENT_notify_transmit_ready (mgr->client,
331                                            GNUNET_SERVER_MAX_MESSAGE_SIZE - 1,
332                                            GNUNET_TIME_UNIT_FOREVER_REL,
333                                            GNUNET_NO,
334                                            &send_next_message,
335                                            mgr);
336 }
337
338
339 /**
340  * Try again to connect to the service.
341  *
342  * @param cls
343  *        Channel handle.
344  */
345 static void
346 schedule_reconnect (void *cls)
347 {
348   struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
349   mgr->reconnect_task = NULL;
350
351   LOG (GNUNET_ERROR_TYPE_DEBUG,
352        "Connecting to %s service.\n", mgr->service_name);
353   GNUNET_assert (NULL == mgr->client);
354   mgr->client = GNUNET_CLIENT_connect (mgr->service_name, mgr->cfg);
355   GNUNET_assert (NULL != mgr->client);
356
357   transmit_next (mgr);
358 }
359
360
361 /**
362  * Connect to service.
363  *
364  * @param cfg
365  *        Configuration to use.
366  * @param service_name
367  *        Service name to connect to.
368  * @param handlers
369  *        Message handlers.
370  *
371  * @return Client manager connection handle.
372  */
373 struct GNUNET_CLIENT_MANAGER_Connection *
374 GNUNET_CLIENT_MANAGER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
375                                const char *service_name,
376                                const struct
377                                GNUNET_CLIENT_MANAGER_MessageHandler *handlers)
378 {
379   struct GNUNET_CLIENT_MANAGER_Connection *
380     mgr = GNUNET_malloc (sizeof (*mgr));
381   mgr->cfg = cfg;
382   mgr->service_name = service_name;
383   mgr->handlers = handlers;
384   mgr->reconnect_delay = GNUNET_TIME_UNIT_ZERO;
385   mgr->reconnect_task = GNUNET_SCHEDULER_add_now (&schedule_reconnect, mgr);
386   return mgr;
387 }
388
389
390 /**
391  * Disconnect from the service.
392  *
393  * @param mgr
394  *        Client manager connection.
395  * @param transmit_queue
396  *        Transmit pending messages in queue before disconnecting.
397  * @param disconnect_cb
398  *        Function called after disconnected from the service.
399  * @param cls
400  *        Closure for @a disconnect_cb.
401  */
402 void
403 GNUNET_CLIENT_MANAGER_disconnect (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
404                                   int transmit_queue,
405                                   GNUNET_ContinuationCallback disconnect_cb,
406                                   void *cls)
407 {
408   LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting (%d)\n", transmit_queue);
409   mgr->disconnect_cb = disconnect_cb;
410   mgr->disconnect_cls = cls;
411   if (NULL != mgr->tmit_head)
412   {
413     if (GNUNET_YES == transmit_queue)
414     {
415       mgr->is_disconnecting = GNUNET_YES;
416       transmit_next (mgr);
417       return;
418     }
419     else
420     {
421       LOG (GNUNET_ERROR_TYPE_DEBUG,
422            "Disconnecting while there are still messages "
423            "in the transmission queue.\n");
424       GNUNET_CLIENT_MANAGER_drop_queue (mgr);
425     }
426   }
427   if (mgr->reconnect_task != NULL)
428   {
429     GNUNET_SCHEDULER_cancel (mgr->reconnect_task);
430     mgr->reconnect_task = NULL;
431   }
432   if (NULL != mgr->client_tmit)
433   {
434     GNUNET_CLIENT_notify_transmit_ready_cancel (mgr->client_tmit);
435     mgr->client_tmit = NULL;
436   }
437   if (NULL != mgr->client)
438   {
439     GNUNET_CLIENT_disconnect (mgr->client);
440     mgr->client = NULL;
441   }
442   if (NULL != mgr->disconnect_cb)
443     mgr->disconnect_cb (mgr->disconnect_cls);
444   GNUNET_free (mgr);
445   LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnected.\n");
446 }
447
448
449 /**
450  * Reschedule connect to the service using exponential back-off.
451  *
452  * @param mgr
453  *        Client manager connection.
454  */
455 void
456 GNUNET_CLIENT_MANAGER_reconnect (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
457 {
458   if (NULL != mgr->reconnect_task)
459     return;
460
461   if (NULL != mgr->client_tmit)
462   {
463     GNUNET_CLIENT_notify_transmit_ready_cancel (mgr->client_tmit);
464     mgr->client_tmit = NULL;
465   }
466   if (NULL != mgr->client)
467   {
468     GNUNET_CLIENT_disconnect (mgr->client);
469     mgr->client = NULL;
470   }
471   mgr->in_receive = GNUNET_NO;
472   LOG (GNUNET_ERROR_TYPE_DEBUG,
473        "Scheduling task to reconnect to service in %s.\n",
474        GNUNET_STRINGS_relative_time_to_string (mgr->reconnect_delay, GNUNET_YES));
475   mgr->reconnect_task =
476     GNUNET_SCHEDULER_add_delayed (mgr->reconnect_delay, &schedule_reconnect, mgr);
477   mgr->reconnect_delay = GNUNET_TIME_STD_BACKOFF (mgr->reconnect_delay);
478 }
479
480
481 /**
482  * Add a message to the end of the transmission queue.
483  *
484  * @param mgr
485  *        Client manager connection.
486  * @param msg
487  *        Message to transmit, should be allocated with GNUNET_malloc() or
488  *        GNUNET_new(), as it is freed with GNUNET_free() after transmission.
489  */
490 void
491 GNUNET_CLIENT_MANAGER_transmit (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
492                                 struct GNUNET_MessageHeader *msg)
493 {
494   struct MessageQueueItem *mqi = GNUNET_malloc (sizeof (*mqi));
495   mqi->msg = GNUNET_copy_message (msg);
496   GNUNET_CONTAINER_DLL_insert_tail (mgr->tmit_head, mgr->tmit_tail, mqi);
497   transmit_next (mgr);
498 }
499
500
501 /**
502  * Add a message to the beginning of the transmission queue.
503  *
504  * @param mgr
505  *        Client manager connection.
506  * @param msg
507  *        Message to transmit, should be allocated with GNUNET_malloc() or
508  *        GNUNET_new(), as it is freed with GNUNET_free() after transmission.
509  */
510 void
511 GNUNET_CLIENT_MANAGER_transmit_now (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
512                                     struct GNUNET_MessageHeader *msg)
513 {
514   struct MessageQueueItem *mqi = GNUNET_malloc (sizeof (*mqi));
515   mqi->msg = GNUNET_copy_message (msg);
516   GNUNET_CONTAINER_DLL_insert (mgr->tmit_head, mgr->tmit_tail, mqi);
517   transmit_next (mgr);
518 }
519
520
521 /**
522  * Drop all queued messages.
523  *
524  * @param mgr
525  *        Client manager connection.
526  */
527 void
528 GNUNET_CLIENT_MANAGER_drop_queue (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
529 {
530   struct MessageQueueItem *cur, *next = mgr->tmit_head;
531   while (NULL != next)
532   {
533     cur = next;
534     next = cur->next;
535     GNUNET_free (cur->msg);
536     GNUNET_free (cur);
537   }
538 }
539
540
541 /**
542  * Obtain client connection handle.
543  *
544  * @param mgr
545  *        Client manager connection.
546  *
547  * @return Client connection handle.
548  */
549 struct GNUNET_CLIENT_Connection *
550 GNUNET_CLIENT_MANAGER_get_client (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
551 {
552   return mgr->client;
553 }
554
555
556 /**
557  * Return user context associated with the given client.
558  * Note: you should probably use the macro (call without the underscore).
559  *
560  * @param mgr
561  *        Client manager connection.
562  * @param size
563  *        Number of bytes in user context struct (for verification only).
564  *
565  * @return User context.
566  */
567 void *
568 GNUNET_CLIENT_MANAGER_get_user_context_ (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
569                                          size_t size)
570 {
571   if ((0 == mgr->user_ctx_size) &&
572       (NULL == mgr->user_ctx))
573     return NULL; /* never set */
574   GNUNET_assert (size == mgr->user_ctx_size);
575   return mgr->user_ctx;
576 }
577
578
579 /**
580  * Set user context to be associated with the given client.
581  * Note: you should probably use the macro (call without the underscore).
582  *
583  * @param mgr
584  *        Client manager connection.
585  * @param ctx
586  *        User context.
587  * @param size
588  *        Number of bytes in user context struct (for verification only).
589  */
590 void
591 GNUNET_CLIENT_MANAGER_set_user_context_ (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
592                                          void *ctx,
593                                          size_t size)
594 {
595   if (NULL == ctx)
596   {
597     mgr->user_ctx_size = 0;
598     mgr->user_ctx = ctx;
599     return;
600   }
601   mgr->user_ctx_size = size;
602   mgr->user_ctx = ctx;
603 }
604
605
606 /**
607  * Get a unique operation ID to distinguish between asynchronous requests.
608  *
609  * @param mgr
610  *        Client manager connection.
611  *
612  * @return Operation ID to use.
613  */
614 uint64_t
615 GNUNET_CLIENT_MANAGER_op_get_next_id (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
616 {
617   return ++mgr->last_op_id;
618 }
619
620
621 /**
622  * Find operation by ID.
623  *
624  * @param mgr
625  *        Client manager connection.
626  * @param op_id
627  *        Operation ID to look up.
628  *
629  * @return Operation, or NULL if not found.
630  */
631 static struct OperationListItem *
632 op_find (struct GNUNET_CLIENT_MANAGER_Connection *mgr, uint64_t op_id)
633 {
634   struct OperationListItem *op = mgr->op_head;
635   while (NULL != op)
636   {
637     if (op->op_id == op_id)
638       return op;
639     op = op->next;
640   }
641   return NULL;
642 }
643
644
645 /**
646  * Find operation by ID.
647  *
648  * @param mgr
649  *        Client manager connection.
650  * @param op_id
651  *        Operation ID to look up.
652  * @param[out] result_cb
653  *        If an operation was found, its result callback is returned here.
654  * @param[out] cls
655  *        If an operation was found, its closure is returned here.
656  *
657  * @return #GNUNET_YES if an operation was found,
658  *         #GNUNET_NO  if not found.
659  */
660 int
661 GNUNET_CLIENT_MANAGER_op_find (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
662                                uint64_t op_id,
663                                GNUNET_ResultCallback *result_cb,
664                                void **cls)
665 {
666   struct OperationListItem *op = op_find (mgr, op_id);
667   if (NULL != op)
668   {
669     *result_cb = op->result_cb;
670     *cls = op->cls;
671     return GNUNET_YES;
672   }
673   return GNUNET_NO;
674 }
675
676
677 /**
678  * Add a new operation.
679  *
680  * @param mgr
681  *        Client manager connection.
682  * @param result_cb
683  *        Function to call with the result of the operation.
684  * @param cls
685  *        Closure for @a result_cb.
686  *
687  * @return ID of the new operation.
688  */
689 uint64_t
690 GNUNET_CLIENT_MANAGER_op_add (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
691                               GNUNET_ResultCallback result_cb,
692                               void *cls)
693 {
694   if (NULL == result_cb)
695     return 0;
696
697   struct OperationListItem *op = GNUNET_malloc (sizeof (*op));
698   op->op_id = GNUNET_CLIENT_MANAGER_op_get_next_id (mgr);
699   op->result_cb = result_cb;
700   op->cls = cls;
701   GNUNET_CONTAINER_DLL_insert_tail (mgr->op_head, mgr->op_tail, op);
702
703   LOG (GNUNET_ERROR_TYPE_DEBUG,
704        "%p Added operation #%" PRIu64 "\n", mgr, op->op_id);
705   return op->op_id;
706 }
707
708
709 /**
710  * Remove an operation, and call its result callback (unless it was cancelled).
711  *
712  *
713  * @param mgr
714  *        Client manager connection.
715  * @param op_id
716  *        Operation ID.
717  * @param result_code
718  *        Result of the operation.
719  * @param data
720  *        Data result of the operation.
721  * @param data_size
722  *        Size of @a data.
723  * @param cancel
724  *        Is the operation cancelled?
725  *        #GNUNET_NO  Not cancelled, result callback is called.
726  *        #GNUNET_YES Cancelled, result callback is not called.
727  *
728  * @return #GNUNET_YES if the operation was found and removed,
729  *         #GNUNET_NO  if the operation was not found.
730  */
731 static int
732 op_result (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
733            uint64_t op_id, int64_t result_code,
734            const void *data, uint16_t data_size, uint8_t cancel)
735 {
736   if (0 == op_id)
737     return GNUNET_NO;
738
739   struct OperationListItem *op = op_find (mgr, op_id);
740   if (NULL == op)
741   {
742     LOG (GNUNET_ERROR_TYPE_WARNING,
743          "Could not find operation #%" PRIu64 "\n", op_id);
744     return GNUNET_NO;
745   }
746
747   GNUNET_CONTAINER_DLL_remove (mgr->op_head, mgr->op_tail, op);
748
749   if (GNUNET_YES != cancel && NULL != op->result_cb)
750     op->result_cb (op->cls, result_code, data, data_size);
751
752   GNUNET_free (op);
753   return GNUNET_YES;
754 }
755
756
757 /**
758  * Call the result callback of an operation and remove it.
759  *
760  * @param mgr
761  *        Client manager connection.
762  * @param op_id
763  *        Operation ID.
764  * @param result_code
765  *        Result of the operation.
766  * @param data
767  *        Data result of the operation.
768  * @param data_size
769  *        Size of @a data.
770  *
771  * @return #GNUNET_YES if the operation was found and removed,
772  *         #GNUNET_NO  if the operation was not found.
773  */
774 int
775 GNUNET_CLIENT_MANAGER_op_result (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
776                                  uint64_t op_id, int64_t result_code,
777                                  const void *data, uint16_t data_size)
778 {
779   LOG (GNUNET_ERROR_TYPE_DEBUG,
780        "%p Received result for operation #%" PRIu64 ": %" PRId64 " (size: %u)\n",
781        mgr, op_id, result_code, data_size);
782   return op_result (mgr, op_id, result_code, data, data_size, GNUNET_NO);
783 }
784
785
786 /**
787  * Cancel an operation.
788  *
789  * @param mgr
790  *        Client manager connection.
791  * @param op_id
792  *        Operation ID.
793  *
794  * @return #GNUNET_YES if the operation was found and removed,
795  *         #GNUNET_NO  if the operation was not found.
796  */
797 int
798 GNUNET_CLIENT_MANAGER_op_cancel (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
799                                  uint64_t op_id)
800 {
801   LOG (GNUNET_ERROR_TYPE_DEBUG,
802        "%p Cancelling operation #%" PRIu64  "\n", mgr, op_id);
803   return op_result (mgr, op_id, 0, NULL, 0, GNUNET_YES);
804 }