expand GNUNET_OS_ProjectData API to also enable de-duplcation of logic for --help
[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, const struct GNUNET_SCHEDULER_TaskContext *tc)
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  * @param tc
345  *        Scheduler context.
346  */
347 static void
348 schedule_reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
349 {
350   struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
351   mgr->reconnect_task = NULL;
352
353   LOG (GNUNET_ERROR_TYPE_DEBUG,
354        "Connecting to %s service.\n", mgr->service_name);
355   GNUNET_assert (NULL == mgr->client);
356   mgr->client = GNUNET_CLIENT_connect (mgr->service_name, mgr->cfg);
357   GNUNET_assert (NULL != mgr->client);
358
359   transmit_next (mgr);
360 }
361
362
363 /**
364  * Connect to service.
365  *
366  * @param cfg
367  *        Configuration to use.
368  * @param service_name
369  *        Service name to connect to.
370  * @param handlers
371  *        Message handlers.
372  *
373  * @return Client manager connection handle.
374  */
375 struct GNUNET_CLIENT_MANAGER_Connection *
376 GNUNET_CLIENT_MANAGER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
377                                const char *service_name,
378                                const struct
379                                GNUNET_CLIENT_MANAGER_MessageHandler *handlers)
380 {
381   struct GNUNET_CLIENT_MANAGER_Connection *
382     mgr = GNUNET_malloc (sizeof (*mgr));
383   mgr->cfg = cfg;
384   mgr->service_name = service_name;
385   mgr->handlers = handlers;
386   mgr->reconnect_delay = GNUNET_TIME_UNIT_ZERO;
387   mgr->reconnect_task = GNUNET_SCHEDULER_add_now (&schedule_reconnect, mgr);
388   return mgr;
389 }
390
391
392 /**
393  * Disconnect from the service.
394  *
395  * @param mgr
396  *        Client manager connection.
397  * @param transmit_queue
398  *        Transmit pending messages in queue before disconnecting.
399  * @param disconnect_cb
400  *        Function called after disconnected from the service.
401  * @param cls
402  *        Closure for @a disconnect_cb.
403  */
404 void
405 GNUNET_CLIENT_MANAGER_disconnect (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
406                                   int transmit_queue,
407                                   GNUNET_ContinuationCallback disconnect_cb,
408                                   void *cls)
409 {
410   LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting (%d)\n", transmit_queue);
411   mgr->disconnect_cb = disconnect_cb;
412   mgr->disconnect_cls = cls;
413   if (NULL != mgr->tmit_head)
414   {
415     if (GNUNET_YES == transmit_queue)
416     {
417       mgr->is_disconnecting = GNUNET_YES;
418       transmit_next (mgr);
419       return;
420     }
421     else
422     {
423       LOG (GNUNET_ERROR_TYPE_DEBUG,
424            "Disconnecting while there are still messages "
425            "in the transmission queue.\n");
426       GNUNET_CLIENT_MANAGER_drop_queue (mgr);
427     }
428   }
429   if (mgr->reconnect_task != NULL)
430   {
431     GNUNET_SCHEDULER_cancel (mgr->reconnect_task);
432     mgr->reconnect_task = NULL;
433   }
434   if (NULL != mgr->client_tmit)
435   {
436     GNUNET_CLIENT_notify_transmit_ready_cancel (mgr->client_tmit);
437     mgr->client_tmit = NULL;
438   }
439   if (NULL != mgr->client)
440   {
441     GNUNET_CLIENT_disconnect (mgr->client);
442     mgr->client = NULL;
443   }
444   if (NULL != mgr->disconnect_cb)
445     mgr->disconnect_cb (mgr->disconnect_cls);
446   GNUNET_free (mgr);
447   LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnected.\n");
448 }
449
450
451 /**
452  * Reschedule connect to the service using exponential back-off.
453  *
454  * @param mgr
455  *        Client manager connection.
456  */
457 void
458 GNUNET_CLIENT_MANAGER_reconnect (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
459 {
460   if (NULL != mgr->reconnect_task)
461     return;
462
463   if (NULL != mgr->client_tmit)
464   {
465     GNUNET_CLIENT_notify_transmit_ready_cancel (mgr->client_tmit);
466     mgr->client_tmit = NULL;
467   }
468   if (NULL != mgr->client)
469   {
470     GNUNET_CLIENT_disconnect (mgr->client);
471     mgr->client = NULL;
472   }
473   mgr->in_receive = GNUNET_NO;
474   LOG (GNUNET_ERROR_TYPE_DEBUG,
475        "Scheduling task to reconnect to service in %s.\n",
476        GNUNET_STRINGS_relative_time_to_string (mgr->reconnect_delay, GNUNET_YES));
477   mgr->reconnect_task =
478     GNUNET_SCHEDULER_add_delayed (mgr->reconnect_delay, &schedule_reconnect, mgr);
479   mgr->reconnect_delay = GNUNET_TIME_STD_BACKOFF (mgr->reconnect_delay);
480 }
481
482
483 /**
484  * Add a message to the end of the transmission queue.
485  *
486  * @param mgr
487  *        Client manager connection.
488  * @param msg
489  *        Message to transmit, should be allocated with GNUNET_malloc() or
490  *        GNUNET_new(), as it is freed with GNUNET_free() after transmission.
491  */
492 void
493 GNUNET_CLIENT_MANAGER_transmit (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
494                                 struct GNUNET_MessageHeader *msg)
495 {
496   struct MessageQueueItem *mqi = GNUNET_malloc (sizeof (*mqi));
497   mqi->msg = GNUNET_copy_message (msg);
498   GNUNET_CONTAINER_DLL_insert_tail (mgr->tmit_head, mgr->tmit_tail, mqi);
499   transmit_next (mgr);
500 }
501
502
503 /**
504  * Add a message to the beginning of the transmission queue.
505  *
506  * @param mgr
507  *        Client manager connection.
508  * @param msg
509  *        Message to transmit, should be allocated with GNUNET_malloc() or
510  *        GNUNET_new(), as it is freed with GNUNET_free() after transmission.
511  */
512 void
513 GNUNET_CLIENT_MANAGER_transmit_now (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
514                                     struct GNUNET_MessageHeader *msg)
515 {
516   struct MessageQueueItem *mqi = GNUNET_malloc (sizeof (*mqi));
517   mqi->msg = GNUNET_copy_message (msg);
518   GNUNET_CONTAINER_DLL_insert (mgr->tmit_head, mgr->tmit_tail, mqi);
519   transmit_next (mgr);
520 }
521
522
523 /**
524  * Drop all queued messages.
525  *
526  * @param mgr
527  *        Client manager connection.
528  */
529 void
530 GNUNET_CLIENT_MANAGER_drop_queue (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
531 {
532   struct MessageQueueItem *cur, *next = mgr->tmit_head;
533   while (NULL != next)
534   {
535     cur = next;
536     next = cur->next;
537     GNUNET_free (cur->msg);
538     GNUNET_free (cur);
539   }
540 }
541
542
543 /**
544  * Obtain client connection handle.
545  *
546  * @param mgr
547  *        Client manager connection.
548  *
549  * @return Client connection handle.
550  */
551 struct GNUNET_CLIENT_Connection *
552 GNUNET_CLIENT_MANAGER_get_client (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
553 {
554   return mgr->client;
555 }
556
557
558 /**
559  * Return user context associated with the given client.
560  * Note: you should probably use the macro (call without the underscore).
561  *
562  * @param mgr
563  *        Client manager connection.
564  * @param size
565  *        Number of bytes in user context struct (for verification only).
566  *
567  * @return User context.
568  */
569 void *
570 GNUNET_CLIENT_MANAGER_get_user_context_ (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
571                                          size_t size)
572 {
573   if ((0 == mgr->user_ctx_size) &&
574       (NULL == mgr->user_ctx))
575     return NULL; /* never set */
576   GNUNET_assert (size == mgr->user_ctx_size);
577   return mgr->user_ctx;
578 }
579
580
581 /**
582  * Set user context to be associated with the given client.
583  * Note: you should probably use the macro (call without the underscore).
584  *
585  * @param mgr
586  *        Client manager connection.
587  * @param ctx
588  *        User context.
589  * @param size
590  *        Number of bytes in user context struct (for verification only).
591  */
592 void
593 GNUNET_CLIENT_MANAGER_set_user_context_ (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
594                                          void *ctx,
595                                          size_t size)
596 {
597   if (NULL == ctx)
598   {
599     mgr->user_ctx_size = 0;
600     mgr->user_ctx = ctx;
601     return;
602   }
603   mgr->user_ctx_size = size;
604   mgr->user_ctx = ctx;
605 }
606
607
608 /**
609  * Get a unique operation ID to distinguish between asynchronous requests.
610  *
611  * @param mgr
612  *        Client manager connection.
613  *
614  * @return Operation ID to use.
615  */
616 uint64_t
617 GNUNET_CLIENT_MANAGER_op_get_next_id (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
618 {
619   return ++mgr->last_op_id;
620 }
621
622
623 /**
624  * Find operation by ID.
625  *
626  * @param mgr
627  *        Client manager connection.
628  * @param op_id
629  *        Operation ID to look up.
630  *
631  * @return Operation, or NULL if not found.
632  */
633 static struct OperationListItem *
634 op_find (struct GNUNET_CLIENT_MANAGER_Connection *mgr, uint64_t op_id)
635 {
636   struct OperationListItem *op = mgr->op_head;
637   while (NULL != op)
638   {
639     if (op->op_id == op_id)
640       return op;
641     op = op->next;
642   }
643   return NULL;
644 }
645
646
647 /**
648  * Find operation by ID.
649  *
650  * @param mgr
651  *        Client manager connection.
652  * @param op_id
653  *        Operation ID to look up.
654  * @param[out] result_cb
655  *        If an operation was found, its result callback is returned here.
656  * @param[out] cls
657  *        If an operation was found, its closure is returned here.
658  *
659  * @return #GNUNET_YES if an operation was found,
660  *         #GNUNET_NO  if not found.
661  */
662 int
663 GNUNET_CLIENT_MANAGER_op_find (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
664                                uint64_t op_id,
665                                GNUNET_ResultCallback *result_cb,
666                                void **cls)
667 {
668   struct OperationListItem *op = op_find (mgr, op_id);
669   if (NULL != op)
670   {
671     *result_cb = op->result_cb;
672     *cls = op->cls;
673     return GNUNET_YES;
674   }
675   return GNUNET_NO;
676 }
677
678
679 /**
680  * Add a new operation.
681  *
682  * @param mgr
683  *        Client manager connection.
684  * @param result_cb
685  *        Function to call with the result of the operation.
686  * @param cls
687  *        Closure for @a result_cb.
688  *
689  * @return ID of the new operation.
690  */
691 uint64_t
692 GNUNET_CLIENT_MANAGER_op_add (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
693                               GNUNET_ResultCallback result_cb,
694                               void *cls)
695 {
696   if (NULL == result_cb)
697     return 0;
698
699   struct OperationListItem *op = GNUNET_malloc (sizeof (*op));
700   op->op_id = GNUNET_CLIENT_MANAGER_op_get_next_id (mgr);
701   op->result_cb = result_cb;
702   op->cls = cls;
703   GNUNET_CONTAINER_DLL_insert_tail (mgr->op_head, mgr->op_tail, op);
704
705   LOG (GNUNET_ERROR_TYPE_DEBUG,
706        "%p Added operation #%" PRIu64 "\n", mgr, op->op_id);
707   return op->op_id;
708 }
709
710
711 /**
712  * Remove an operation, and call its result callback (unless it was cancelled).
713  *
714  *
715  * @param mgr
716  *        Client manager connection.
717  * @param op_id
718  *        Operation ID.
719  * @param result_code
720  *        Result of the operation.
721  * @param data
722  *        Data result of the operation.
723  * @param data_size
724  *        Size of @a data.
725  * @param cancel
726  *        Is the operation cancelled?
727  *        #GNUNET_NO  Not cancelled, result callback is called.
728  *        #GNUNET_YES Cancelled, result callback is not called.
729  *
730  * @return #GNUNET_YES if the operation was found and removed,
731  *         #GNUNET_NO  if the operation was not found.
732  */
733 static int
734 op_result (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
735            uint64_t op_id, int64_t result_code,
736            const void *data, uint16_t data_size, uint8_t cancel)
737 {
738   if (0 == op_id)
739     return GNUNET_NO;
740
741   struct OperationListItem *op = op_find (mgr, op_id);
742   if (NULL == op)
743   {
744     LOG (GNUNET_ERROR_TYPE_WARNING,
745          "Could not find operation #%" PRIu64 "\n", op_id);
746     return GNUNET_NO;
747   }
748
749   GNUNET_CONTAINER_DLL_remove (mgr->op_head, mgr->op_tail, op);
750
751   if (GNUNET_YES != cancel && NULL != op->result_cb)
752     op->result_cb (op->cls, result_code, data, data_size);
753
754   GNUNET_free (op);
755   return GNUNET_YES;
756 }
757
758
759 /**
760  * Call the result callback of an operation and remove it.
761  *
762  * @param mgr
763  *        Client manager connection.
764  * @param op_id
765  *        Operation ID.
766  * @param result_code
767  *        Result of the operation.
768  * @param data
769  *        Data result of the operation.
770  * @param data_size
771  *        Size of @a data.
772  *
773  * @return #GNUNET_YES if the operation was found and removed,
774  *         #GNUNET_NO  if the operation was not found.
775  */
776 int
777 GNUNET_CLIENT_MANAGER_op_result (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
778                                  uint64_t op_id, int64_t result_code,
779                                  const void *data, uint16_t data_size)
780 {
781   LOG (GNUNET_ERROR_TYPE_DEBUG,
782        "%p Received result for operation #%" PRIu64 ": %" PRId64 " (size: %u)\n",
783        mgr, op_id, result_code, data_size);
784   return op_result (mgr, op_id, result_code, data, data_size, GNUNET_NO);
785 }
786
787
788 /**
789  * Cancel an operation.
790  *
791  * @param mgr
792  *        Client manager connection.
793  * @param op_id
794  *        Operation ID.
795  *
796  * @return #GNUNET_YES if the operation was found and removed,
797  *         #GNUNET_NO  if the operation was not found.
798  */
799 int
800 GNUNET_CLIENT_MANAGER_op_cancel (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
801                                  uint64_t op_id)
802 {
803   LOG (GNUNET_ERROR_TYPE_DEBUG,
804        "%p Cancelling operation #%" PRIu64  "\n", mgr, op_id);
805   return op_result (mgr, op_id, 0, NULL, 0, GNUNET_YES);
806 }