use upoll for event loop
[src/app-framework-binder.git] / plugins / afm-main-plugin / utils-sbus.c
1 /*
2  Copyright 2015 IoT.bzh
3
4  author: José Bollo <jose.bollo@iot.bzh>
5
6  Licensed under the Apache License, Version 2.0 (the "License");
7  you may not use this file except in compliance with the License.
8  You may obtain a copy of the License at
9
10      http://www.apache.org/licenses/LICENSE-2.0
11
12  Unless required by applicable law or agreed to in writing, software
13  distributed under the License is distributed on an "AS IS" BASIS,
14  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  See the License for the specific language governing permissions and
16  limitations under the License.
17 */
18 #define _GNU_SOURCE
19
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <errno.h>
23 #include <string.h>
24 #include <poll.h>
25 #include <assert.h>
26
27 #include <dbus/dbus.h>
28
29 #include "utils-sbus.h"
30
31 /*
32  * errors messages generated by sbus
33  */
34 static const char invalid_request_string[] = "invalid request";
35 static const char out_of_memory_string[] = "out of memory";
36
37 /*
38  * structure for handled messages
39  */
40 struct sbusmsg {
41         DBusMessage *message;       /* message of the message */
42         DBusConnection *connection; /* connection of the message */
43 };
44
45 /*
46  * structure for services
47  */
48 struct sbus_service {
49         struct sbus_service *next;      /* link to the next service */
50         char *destination;              /* destination for the service */
51         char *path;             /* path for the service */
52         char *iface;            /* iface for the service */
53         char *member;           /* member for the service */
54         void (*oncall) (struct sbusmsg *, const char *, void *);
55                                 /* callback */
56         void *closure;          /* closure for the callbacks */
57 };
58
59 /*
60  * structure for signals
61  */
62 struct sbus_signal {
63         struct sbus_signal *next;       /* link to the next signal */
64         char *sender;           /* expected sender of the signal */
65         char *path;             /* expected path of the signal */
66         char *iface;            /* expected iface of the signal */
67         char *member;           /* expected member of the signal */
68         void (*onsignal) (const struct sbusmsg *, const char *, void *);
69                                 /* callback */
70         void *closure;          /* closure for the callbacks */
71 };
72
73 /*
74  * structure for asynchronous requests (resp-onse w-aiter)
75  */
76 struct srespw {
77         struct srespw *next;    /* next asynchronous */
78         dbus_uint32_t serial;   /* serial dbus number */
79         void *closure;          /* closure for the callbacks */
80         void (*onresp) (int, const char *, void *);
81                                 /* callback */
82 };
83
84 /*
85  * structure for synchronous calls
86  */
87 struct respsync {
88         int replied;    /* boolean flag indicating reply */
89         int status;     /* received status */
90         char *value;    /* copy of the returned value */
91 };
92
93 /*
94  * structure for handling either client or server sbus on dbus
95  */
96 struct sbus {
97         int refcount;                   /* referenced how many time */
98         DBusConnection *connection;     /* connection to DBU */
99         const struct sbus_itf *itf;     /* interface to the main loop */
100         void *itfclo;
101         struct sbus_service *services;  /* first service */
102         struct sbus_signal *signals;    /* first signal */
103         struct srespw *waiters;         /* first response waiter */
104         
105 };
106
107 static struct sbus system_sbus;
108 static struct sbus session_sbus;
109
110 /*********************** STATIC COMMON METHODS *****************/
111
112 /*
113  * Frees the ressources attached to a message
114  */
115 static inline void free_sbusmsg(struct sbusmsg *smsg)
116 {
117         dbus_message_unref(smsg->message);
118         dbus_connection_unref(smsg->connection);
119         free(smsg);
120 }
121
122 /*
123  * Replies the error "out of memory".
124  * This function is intended to be used in services when an
125  * allocation fails. Thus, it set errno to ENOMEM and
126  * returns -1.
127  */
128 static inline int reply_out_of_memory(struct sbusmsg *smsg)
129 {
130         sbus_reply_error(smsg, out_of_memory_string);
131         errno = ENOMEM;
132         return -1;
133 }
134
135 /*
136  * Checks if the incoming 'message' matches the interface
137  * linked to 'sbus'.
138  *
139  * Returns 1 if it matches or 0 wether it does not matches.
140  */
141 /*
142 static int matchitf(struct sbus *sbus, DBusMessage * message)
143 {
144         const char *itf = dbus_message_get_interface(message);
145         return itf != NULL && !strcmp(itf, sbus->name);
146 }
147 */
148
149 /*
150  * Callback function for synchronous calls.
151  * This function fills the respsync structure pointed by 'data'
152  * with the copy of the answer.
153  */
154 static void sync_of_replies(int status, const char *value, struct respsync *s)
155 {
156         s->status = status;
157         s->value = status ? NULL : strdup(value ? value : "");
158         s->replied = 1;
159 }
160
161 /*
162  * Creates and returns the rule for 'signal'.
163  */
164 static char *rule_of_signal(struct sbus_signal *signal)
165 {
166         char *rule;
167         return asprintf(&rule,
168                         "type='signal%s%s%s%s%s%s%s%s'",
169                         signal->sender ? "',sender='" : "",
170                                 signal->sender ? signal->sender : "",
171                         signal->path   ? "',path='" : "",
172                                 signal->path   ? signal->path : "",
173                         signal->iface  ? "',interface='" : "",
174                                 signal->iface  ? signal->iface : "",
175                         signal->member  ? "',member='" : "",
176                                 signal->member  ? signal->member : ""
177                 ) < 0 ? NULL : rule;
178 }
179
180 /*********************** STATIC DBUS MESSAGE HANDLING *****************/
181
182 /*
183  * Handles incomming responses 'message' on 'sbus'. Response are
184  * either expected if 'iserror' == 0 or errors if 'iserror' != 0.
185  *
186  * Returns DBUS_HANDLER_RESULT_HANDLED or DBUS_HANDLER_RESULT_NOT_YET_HANDLED
187  * as defined by the dbus function 'dbus_connection_add_filter'.
188  */
189 static DBusHandlerResult incoming_resp(
190                 struct sbus *sbus,
191                 DBusMessage * message,
192                 int iserror)
193 {
194         int status;
195         const char *str;
196         struct srespw *jrw, **prv;
197         dbus_uint32_t serial;
198
199         /* search for the waiter */
200         serial = dbus_message_get_reply_serial(message);
201         prv = &sbus->waiters;
202         while ((jrw = *prv) != NULL) {
203                 if (jrw->serial == serial)
204                         goto found;
205                 prv = &jrw->next;
206         }
207         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
208
209         /* treat it */
210  found:
211         *prv = jrw->next;
212         if (jrw->onresp) {
213                 /* retrieve the string value */
214                 if (dbus_message_get_args
215                     (message, NULL, DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID))
216                         status = 0;
217                 else {
218                         status = -1;
219                         str = NULL;
220                 }
221                 /* call now */
222                 jrw->onresp(iserror ? -1 : status, str, jrw->closure);
223         }
224         free(jrw);
225         return DBUS_HANDLER_RESULT_HANDLED;
226 }
227
228 /*
229  * Handles incomming on 'sbus' method calls for 'message'.
230  *
231  * Returns DBUS_HANDLER_RESULT_HANDLED or DBUS_HANDLER_RESULT_NOT_YET_HANDLED
232  * as defined by the dbus function 'dbus_connection_add_filter'.
233  */
234 static DBusHandlerResult incoming_call(
235                 struct sbus *sbus,
236                 DBusMessage * message)
237 {
238         struct sbus_service *service;
239         struct sbusmsg *smsg;
240         const char *str;
241
242         /* search for the service */
243         service = sbus->services;
244         while (service != NULL) {
245                 if ((service->destination == NULL || !strcmp(service->destination, dbus_message_get_destination(message)))
246                  && (service->path == NULL || !strcmp(service->path, dbus_message_get_path(message)))
247                  && (service->iface == NULL || !strcmp(service->iface, dbus_message_get_interface(message)))
248                  && (service->member == NULL || !strcmp(service->member, dbus_message_get_member(message))))
249                         goto found;
250                 service = service->next;
251         }
252         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
253
254  found:
255         /* creates and init the smsg structure */
256         smsg = malloc(sizeof *smsg);
257         if (smsg == NULL)
258                 return DBUS_HANDLER_RESULT_NEED_MEMORY;
259         smsg->message = dbus_message_ref(message);
260         smsg->connection = dbus_connection_ref(sbus->connection);
261
262         /* retrieve the string parameter of the message */
263         if (!dbus_message_get_args
264             (message, NULL, DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID))
265                 goto invalid_request;
266
267         /* handling strings only */
268         service->oncall(smsg, str, service->closure);
269         return DBUS_HANDLER_RESULT_HANDLED;
270
271 invalid_request:
272         sbus_reply_error(smsg, invalid_request_string);
273         return DBUS_HANDLER_RESULT_HANDLED;
274 }
275
276 /*
277  * Handles incomming on 'sbus' signal propagated with 'message'.
278  *
279  * This is a design choice to ignore invalid signals.
280  *
281  * Returns DBUS_HANDLER_RESULT_HANDLED or DBUS_HANDLER_RESULT_NOT_YET_HANDLED
282  * as defined by the dbus function 'dbus_connection_add_filter'.
283  */
284 static DBusHandlerResult incoming_signal(
285                 struct sbus *sbus,
286                 DBusMessage * message)
287 {
288         DBusHandlerResult result;
289         struct sbus_signal *signal;
290         struct sbusmsg smsg;
291         const char *str;
292
293         /* retrieve the string value */
294         result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
295         if (!dbus_message_get_args(message, NULL,
296                                 DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID))
297                 goto end;
298
299         /* search a handler */
300         smsg.message = message;
301         smsg.connection = NULL;
302         signal = sbus->signals;
303         while (signal != NULL) {
304                 if ((signal->path == NULL || !strcmp(signal->path, dbus_message_get_path(message)))
305                  && (signal->iface == NULL || !strcmp(signal->iface, dbus_message_get_interface(message)))
306                  && (signal->member == NULL || !strcmp(signal->member, dbus_message_get_member(message)))) {
307                         signal->onsignal(&smsg, str, signal->closure);
308                         result = DBUS_HANDLER_RESULT_HANDLED;
309                 }
310                 signal = signal->next;
311         }
312  end:
313         return result;
314 }
315
316 /*
317  * Filters incomming messages as defined by the dbus function
318  * 'dbus_connection_add_filter'.
319  * Returns DBUS_HANDLER_RESULT_HANDLED or DBUS_HANDLER_RESULT_NOT_YET_HANDLED.
320  */
321 static DBusHandlerResult incoming(
322                 DBusConnection * connection,
323                 DBusMessage * message,
324                 struct sbus *sbus)
325 {
326         switch (dbus_message_get_type(message)) {
327         case DBUS_MESSAGE_TYPE_METHOD_CALL:
328                 return incoming_call(sbus, message);
329         case DBUS_MESSAGE_TYPE_METHOD_RETURN:
330                 return incoming_resp(sbus, message, 0);
331         case DBUS_MESSAGE_TYPE_ERROR:
332                 return incoming_resp(sbus, message, 1);
333         case DBUS_MESSAGE_TYPE_SIGNAL:
334                 return incoming_signal(sbus, message);
335         }
336         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
337 }
338
339 /*********************** STATIC DBUS WATCH/POLLING INTERFACE **********/
340
341 struct swatch {
342         DBusConnection *connection;
343         DBusWatch *watch;
344         void *hndl;
345 };
346
347 static void on_hangup(struct swatch *s)
348 {
349         dbus_watch_handle(s->watch, DBUS_WATCH_HANGUP);
350         while (dbus_connection_dispatch(s->connection) == DBUS_DISPATCH_DATA_REMAINS);
351 }
352
353 static void on_readable(struct swatch *s)
354 {
355         dbus_watch_handle(s->watch, DBUS_WATCH_READABLE);
356         while (dbus_connection_dispatch(s->connection) == DBUS_DISPATCH_DATA_REMAINS);
357 }
358
359 static void on_writable(struct swatch *s)
360 {
361         dbus_watch_handle(s->watch, DBUS_WATCH_WRITABLE);
362         while (dbus_connection_dispatch(s->connection) == DBUS_DISPATCH_DATA_REMAINS);
363 }
364
365 /*
366  * DBUS Callback for removing a 'watch'.
367  * See function 'dbus_connection_set_watch_functions'
368  */
369 static void watchdel(DBusWatch *watch, struct sbus *sbus)
370 {
371         struct swatch *s = dbus_watch_get_data(watch);
372         sbus->itf->close(s->hndl);
373         free(s);
374 }
375
376 /*
377  * DBUS Callback for changing a 'watch'.
378  * See function 'dbus_connection_set_watch_functions'
379  */
380 static void watchtoggle(DBusWatch *watch, struct sbus *sbus)
381 {
382         struct swatch *s = dbus_watch_get_data(watch);
383         int enabled = (int)dbus_watch_get_enabled(watch);
384         unsigned int flags = dbus_watch_get_flags(watch);
385         if (flags & DBUS_WATCH_READABLE)
386                 sbus->itf->on_readable(s->hndl, enabled ? (void*)on_readable : NULL);
387         if (flags & DBUS_WATCH_WRITABLE)
388                 sbus->itf->on_writable(s->hndl, enabled ? (void*)on_writable : NULL);
389 }
390
391
392 /*
393  * DBUS Callback for adding a 'watch'.
394  * See function 'dbus_connection_set_watch_functions'
395  */
396 static dbus_bool_t watchadd(DBusWatch *watch, struct sbus *sbus)
397 {
398         int fd;
399         struct swatch *s;
400
401         s = malloc(sizeof *s);
402         if (s == NULL)
403                 goto error;
404         fd = dbus_watch_get_unix_fd(watch);
405         s->hndl = sbus->itf->open(fd, s, sbus->itfclo);
406         if (s->hndl == NULL)
407                 goto error2;
408         s->watch = watch;
409         s->connection = sbus->connection;
410         dbus_watch_set_data(watch, s, NULL);
411         sbus->itf->on_hangup(s->hndl, (void*)on_hangup);
412         watchtoggle(watch, sbus);
413         return TRUE;
414  error2:
415         free(s);
416  error:
417         return FALSE;
418 }
419
420 /*
421  * Creates a 'sbus' bound the 'path' and it derived names and linked
422  * either to the DBUS SYSTEM when 'session' is nul or to the DBUS SESSION
423  * if 'session' is not nul.
424  *
425  * The parameter 'path' is intended to be the path of a DBUS single object.
426  * Single means that it exists only one instance of the object on the
427  * given bus. That path implies 2 derived DBUS names:
428  *   1. the destination name of the program that handles the object
429  *   2. the interface name of the object
430  * These names are derived by removing the heading slash (/) and
431  * by replacing all occurences of slashes by dots.
432  * For example, passing path = /a/b/c means that the object /a/b/c is
433  * handled by the destination a.b.c and replies to the interface a.b.c
434  *
435  * Returns the created sbus or NULL in case of error.
436  */
437 static struct sbus *get_sbus(const struct sbus_itf *itf, void *itfclo, struct sbus *sbus)
438 {
439         /* create the sbus object */
440         if (sbus->refcount > 0) {
441                 if (itf != sbus->itf)
442                         goto error;
443                 goto success;
444         }
445
446         /* connect and init */
447         sbus->connection = dbus_bus_get(sbus == &session_sbus ? DBUS_BUS_SESSION
448                                                 : DBUS_BUS_SYSTEM, NULL);
449         if (sbus->connection == NULL)
450                 goto error;
451
452         sbus->itf = itf;
453         sbus->itfclo = itfclo;
454         if (!dbus_connection_add_filter(sbus->connection, (void*)incoming, sbus, NULL)
455          || !dbus_connection_set_watch_functions(sbus->connection, (void*)watchadd,
456                                         (void*)watchdel, (void*)watchtoggle, sbus, NULL)) 
457                 goto error2;
458
459  success:
460         sbus->refcount++;
461         return sbus;
462
463  error2:
464         dbus_connection_unref(sbus->connection);
465         sbus->connection = NULL;
466  error:
467         return NULL;
468 }
469
470 /********************* MAIN FUNCTIONS *****************************************/
471
472 /*
473  * Creates a 'sbus' bound to DBUS system using 'path' and returns it.
474  * See 'create_sbus'
475  */
476 struct sbus *sbus_system(const struct sbus_itf *itf, void *itfclo)
477 {
478         return get_sbus(itf, itfclo, &system_sbus);
479 }
480
481 /*
482  * Creates a 'sbus' bound to DBUS session using 'path' and returns it.
483  * See 'create_sbus'
484  */
485 struct sbus *sbus_session(const struct sbus_itf *itf, void *itfclo)
486 {
487         return get_sbus(itf, itfclo, &session_sbus);
488 }
489
490 /*
491  * Adds one reference to 'sbus'.
492  */
493 void sbus_addref(struct sbus *sbus)
494 {
495         sbus->refcount++;
496 }
497
498 /*
499  * Removes one reference to 'sbus'. Destroys 'sbus' and it related
500  * data if the count of references decrease to zero.
501  */
502 void sbus_unref(struct sbus *sbus)
503 {
504         struct srespw *w;
505         if (!--sbus->refcount) {
506                 while (sbus->services != NULL)
507                         sbus_remove_service(sbus, sbus->services);
508                 while (sbus->signals != NULL)
509                         sbus_remove_signal(sbus, sbus->signals);
510                 if (sbus->connection != NULL) {
511                         dbus_connection_unref(sbus->connection);
512                         sbus->connection = NULL;
513                 }
514                 while ((w = sbus->waiters)) {
515                         sbus->waiters = w->next;
516                         if (w->onresp)
517                                 w->onresp(-1, "cancelled", w->closure);
518                         free(w);
519                 }
520         }
521 }
522
523 /*
524  * Sends from 'sbus' the signal of 'member' handling the string 'content'.
525  *
526  * Returns 0 in case of success or -1 in case of error.
527  */
528 int sbus_send_signal(struct sbus *sbus, const char *sender, const char *path, const char *iface, const char *member, const char *content)
529 {
530         int rc = -1;
531         DBusMessage *message;
532
533         message = dbus_message_new_signal(path, iface, member);
534         if (message == NULL)
535                 goto error;
536
537         if (sender != NULL && !dbus_message_set_sender(message, sender))
538                 goto error2;
539
540         if (!dbus_message_append_args(message, DBUS_TYPE_STRING, &content,
541                                          DBUS_TYPE_INVALID))
542                 goto error2;
543
544         if (dbus_connection_send(sbus->connection, message, NULL))
545                 rc = 0;
546
547         dbus_message_unref(message);
548         return rc;
549
550  error2:
551         dbus_message_unref(message);
552
553  error:
554         errno = ENOMEM;
555         return -1;
556 }
557
558 /*
559  * Asynchronous call to 'method' of 'sbus' passing the string 'query'.
560  * On response, the function 'onresp' is called with the returned string
561  * value and the closure 'closure'.
562  * The function 'onresp' is invoked with 3 parameters:
563  *   1. int: 0 if no error or -1 if error.
564  *   2. const char *: the returned string (might be NULL if error)
565  *   3. void *: the closure 'closure'
566  *
567  * Returns 0 in case of success or -1 in case of error.
568  */
569 int sbus_call(
570                 struct sbus *sbus,
571                 const char *destination,
572                 const char *path,
573                 const char *iface,
574                 const char *method,
575                 const char *query,
576                 void (*onresp) (int, const char *, void *),
577                 void *closure)
578 {
579         DBusMessage *msg;
580         struct srespw *resp;
581
582         /* allocates the response structure */
583         resp = malloc(sizeof *resp);
584         if (resp == NULL) {
585                 errno = ENOMEM;
586                 goto error;
587         }
588
589         /* creates the message */
590         msg = dbus_message_new_method_call(destination, path, iface, method);
591         if (msg == NULL) {
592                 errno = ENOMEM;
593                 goto error2;
594         }
595
596         /* fill it */
597         if (!dbus_message_append_args
598             (msg, DBUS_TYPE_STRING, &query, DBUS_TYPE_INVALID)) {
599                 errno = ENOMEM;
600                 goto error3;
601         }
602
603         /* send it */
604         if (!dbus_connection_send(sbus->connection, msg, &resp->serial)) {
605                 /* TODO: which error? */
606                 goto error3;
607         }
608
609         /* release the message that is not more used */
610         dbus_message_unref(msg);
611
612         /* fulfill the response structure */
613         resp->closure = closure;
614         resp->onresp = onresp;
615
616         /* links the response to list of reponse waiters */
617         resp->next = sbus->waiters;
618         sbus->waiters = resp;
619         return 0;
620
621  error3:
622         dbus_message_unref(msg);
623  error2:
624         free(resp);
625  error:
626         return -1;
627 }
628
629 /*
630  * Synchronous call to 'method' of 'sbus' passing the string 'query'.
631  * The returned string response is returned.
632  *
633  * Returns the string response or NULL in case of error.
634  */
635 char *sbus_call_sync(
636                 struct sbus *sbus,
637                 const char *destination,
638                 const char *path,
639                 const char *iface,
640                 const char *method,
641                 const char *query)
642 {
643         struct respsync synchro;
644         synchro.value = NULL;
645         synchro.replied = sbus_call(sbus, destination, path,
646                                     iface, method, query,
647                                     (void*)sync_of_replies, &synchro);
648         while (!synchro.replied)
649                 if (sbus->itf->wait(-1, sbus->itfclo) != 0)
650                         return NULL;
651         return synchro.value;
652 }
653
654
655 /*
656  * Records for 'sbus' the string signal handler 'onsig' with closure 'closure'
657  * for the signal of 'member'.
658  * The callback handler is called with 2 arguments:
659  *   1. char *: the string parameter associated to the signal
660  *   2. void *: the closure closure.
661  *
662  * Returns 0 in case of success or -1 otherwise.
663  */
664 struct sbus_signal *sbus_add_signal(
665                 struct sbus *sbus,
666                 const char *sender,
667                 const char *path,
668                 const char *iface,
669                 const char *member,
670                 void (*onsignal) (const struct sbusmsg *, const char *, void *),
671                 void *closure)
672 {
673         char *rule;
674         struct sbus_signal *signal;
675
676         /* allocation */
677         signal = calloc(1, sizeof *signal);
678         if (signal == NULL)
679                 goto error;
680         if (sender != NULL) {
681                 signal->sender = strdup(sender);
682                 if (!signal->sender)
683                         goto error2;
684         }
685         if (path != NULL) {
686                 signal->path = strdup(path);
687                 if (!signal->path)
688                         goto error2;
689         }
690         if (iface != NULL) {
691                 signal->iface = strdup(iface);
692                 if (!signal->iface)
693                         goto error2;
694         }
695         if (member != NULL) {
696                 signal->member = strdup(member);
697                 if (!signal->member)
698                         goto error2;
699         }
700
701         /* record the signal */
702         rule = rule_of_signal(signal);
703         if (rule == NULL)
704                 goto error2;
705         dbus_bus_add_match(sbus->connection, rule, NULL);
706         free(rule);
707
708         /* record the signal */
709         signal->onsignal = onsignal;
710         signal->closure = closure;
711         signal->next = sbus->signals;
712         sbus->signals = signal;
713
714         return signal;
715
716  error2:
717         free(signal->sender);
718         free(signal->path);
719         free(signal->iface);
720         free(signal->member);
721         free(signal);
722  error:
723         errno = ENOMEM;
724         return NULL;
725 }
726
727 /*
728  * Removes the 'signal' handler from 'sbus'
729  * Returns 0 in case of success or -1 in case of error.
730  */
731 int sbus_remove_signal(struct sbus *sbus, struct sbus_signal *signal)
732 {
733         char *rule;
734         struct sbus_signal **it;
735
736         it = &sbus->signals;
737         while (*it != NULL) {
738                 if (*it == signal)
739                         goto found;
740                 it = &(*it)->next;
741         }
742         errno = EINVAL;
743         return -1;
744
745 found:
746         rule = rule_of_signal(signal);
747         if (rule != NULL) {
748                 dbus_bus_remove_match(sbus->connection, rule, NULL);
749                 free(rule);
750         }
751         *it = signal->next;
752         free(signal->sender);
753         free(signal->path);
754         free(signal->iface);
755         free(signal->member);
756         free(signal);
757         return 0;
758 }
759
760 /*
761  * Start to serve: activate services declared for 'sbus'.
762  * This function, in fact, declares 'sbus' as the receiver
763  * for calls to the destination derived from the path set at
764  * 'sbus' creation.
765  * It also allows 'sbus' to emit signals of that origin.
766  *
767  * Returns 0 in case of success or -1 in case of error.
768  */
769 int sbus_add_name(struct sbus *sbus, const char *name)
770 {
771         int status = dbus_bus_request_name(sbus->connection, name,
772                                         DBUS_NAME_FLAG_DO_NOT_QUEUE, NULL);
773         switch (status) {
774         case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
775         case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
776                 return 0;
777         case DBUS_REQUEST_NAME_REPLY_EXISTS:
778         case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
779         default:
780                 errno = EADDRINUSE;
781                 return -1;
782         }
783 }
784
785 /*
786  * Adds to 'sbus' a service handling calls to the 'method' using
787  * the callback 'oncall' and the closure value 'closure'.
788  *
789  * The callback 'oncall' is invoked for handling incoming method
790  * calls. It receives 3 parameters:
791  *   1. struct sbusmsg *: a handler to data to be used for replying
792  *   2. const char *: the received string
793  *   3. void *: the closure 'closure' set by this function
794  *
795  * Returns 0 in case of success or -1 in case of error.
796  */
797 struct sbus_service *sbus_add_service(
798                 struct sbus *sbus,
799                 const char *destination,
800                 const char *path,
801                 const char *iface,
802                 const char *member,
803                 void (*oncall) (struct sbusmsg *, const char *, void *),
804                 void *closure)
805 {
806         struct sbus_service *service;
807
808         /* allocation */
809         service = calloc(1, sizeof *service);
810         if (service == NULL)
811                 goto error;
812         if (destination != NULL) {
813                 service->destination = strdup(destination);
814                 if (!service->destination)
815                         goto error2;
816         }
817         if (path != NULL) {
818                 service->path = strdup(path);
819                 if (!service->path)
820                         goto error2;
821         }
822         if (iface != NULL) {
823                 service->iface = strdup(iface);
824                 if (!service->iface)
825                         goto error2;
826         }
827         if (member != NULL) {
828                 service->member = strdup(member);
829                 if (!service->member)
830                         goto error2;
831         }
832
833         /* record the service */
834         service->oncall = oncall;
835         service->closure = closure;
836         service->next = sbus->services;
837         sbus->services = service;
838
839         return service;
840
841  error2:
842         free(service->destination);
843         free(service->path);
844         free(service->iface);
845         free(service->member);
846         free(service);
847  error:
848         errno = ENOMEM;
849         return NULL;
850 }
851
852 /*
853  * Removes the 'service' handler from 'sbus'
854  * Returns 0 in case of success or -1 in case of error.
855  */
856 int sbus_remove_service(struct sbus *sbus, struct sbus_service *service)
857 {
858         struct sbus_service **it;
859
860         it = &sbus->services;
861         while (*it != NULL) {
862                 if (*it == service)
863                         goto found;
864                 it = &(*it)->next;
865         }
866         errno = EINVAL;
867         return -1;
868
869 found:
870         *it = service->next;
871         free(service->destination);
872         free(service->path);
873         free(service->iface);
874         free(service->member);
875         free(service);
876         return 0;
877 }
878
879 const char *sbus_sender(const struct sbusmsg *smsg)
880 {
881         return dbus_message_get_sender(smsg->message);
882 }
883
884 const char *sbus_destination(const struct sbusmsg *smsg)
885 {
886         return dbus_message_get_destination(smsg->message);
887 }
888
889 const char *sbus_path(const struct sbusmsg *smsg)
890 {
891         return dbus_message_get_path(smsg->message);
892 }
893
894 const char *sbus_interface(const struct sbusmsg *smsg)
895 {
896         return dbus_message_get_interface(smsg->message);
897 }
898
899 const char *sbus_member(const struct sbusmsg *smsg)
900 {
901         return dbus_message_get_member(smsg->message);
902 }
903
904 /*
905  * Replies an error of string 'error' to the request handled by 'smsg'.
906  * Also destroys the request 'smsg' that must not be used later.
907  *
908  * Returns 0 in case of success or -1 in case of error.
909  */
910 int sbus_reply_error(struct sbusmsg *smsg, const char *error)
911 {
912         int rc = -1;
913         DBusMessage *message;
914
915         message = dbus_message_new_error(smsg->message, DBUS_ERROR_FAILED,
916                                                                 error);
917         if (message == NULL)
918                 errno = ENOMEM;
919         else {
920                 if (dbus_connection_send(smsg->connection, message, NULL))
921                         rc = 0;
922                 dbus_message_unref(message);
923         }
924         free_sbusmsg(smsg);
925         return rc;
926 }
927
928 /*
929  * Replies normally the string 'reply' to the request handled by 'smsg'.
930  * Also destroys the request 'smsg' that must not be used later.
931  *
932  * Returns 0 in case of success or -1 in case of error.
933  */
934 int sbus_reply(struct sbusmsg *smsg, const char *reply)
935 {
936         int rc = -1;
937         DBusMessage *message;
938
939         message = dbus_message_new_method_return(smsg->message);
940         if (message == NULL)
941                 return reply_out_of_memory(smsg);
942
943         if (!dbus_message_append_args
944             (message, DBUS_TYPE_STRING, &reply, DBUS_TYPE_INVALID)) {
945                 dbus_message_unref(message);
946                 return reply_out_of_memory(smsg);
947         }
948
949         if (dbus_connection_send(smsg->connection, message, NULL))
950                 rc = 0;
951         dbus_message_unref(message);
952         free_sbusmsg(smsg);
953         return rc;
954 }
955
956 /****************** FEW LITTLE TESTS *****************************************/
957
958 #if defined(SERVER)||defined(CLIENT)
959 #include <stdio.h>
960 #include <unistd.h>
961 #include "utils-upoll.h"
962
963 static int mwait(int timeout, void *closure)
964 {
965         upoll_wait(timeout);
966         return 0;
967 }
968
969 static const struct sbus_itf uitf = {
970         .wait = (void*)mwait,
971         .open = (void*)upoll_open,
972         .on_readable = (void*)upoll_on_readable,
973         .on_writable = (void*)upoll_on_writable,
974         .on_hangup = (void*)upoll_on_hangup,
975         .close = (void*)upoll_close
976 };
977
978 static const char name[] = "org.toto";
979 static const char path[] = "/org/toto";
980 static const char iface[] = "org.toto";
981 static struct sbus *sbus;
982
983 #ifdef SERVER
984 void ping(struct sbusmsg *smsg, const char *request, void *unused)
985 {
986         printf("ping(%s) -> %s\n", request, request);
987         sbus_reply(smsg, request);
988 }
989
990 void incr(struct sbusmsg *smsg, const char *request, void *unused)
991 {
992         static int counter = 0;
993         char res[150];
994         sprintf(res, "%d", ++counter);
995         printf("incr(%s) -> %s\n", request, res);
996         sbus_reply(smsg, res);
997         sbus_send_signal(sbus, name, path, iface, "incremented", res);
998 }
999
1000 int main()
1001 {
1002         int s1, s2, s3;
1003         sbus = sbus_session(&uitf, NULL);
1004         s3 = !sbus_add_name(sbus, name);
1005         s1 = !!sbus_add_service(sbus, name, path, iface, "ping", ping, NULL);
1006         s2 = !!sbus_add_service(sbus, name, path, iface, "incr", incr, NULL);
1007         printf("started %d %d %d\n", s1, s2, s3);
1008         while (1) upoll_wait(-1);
1009 }
1010 #endif
1011
1012 #ifdef CLIENT
1013 void onresp(int status, const char *response, void *closure)
1014 {
1015         printf("resp: %d, %s, %s\n", status, (const char *)closure, response);
1016 }
1017
1018 void signaled(const struct sbusmsg *req, const char *data, void *closure)
1019 {
1020         printf("signaled with {%s}/%s\n", data, (const char*)closure);
1021 }
1022
1023 int main()
1024 {
1025         int i = 10;
1026         sbus = sbus_session(&uitf, NULL);
1027         sbus_add_signal(sbus, name, path, iface, "incremented", signaled, "signal");
1028         while (i--) {
1029                 sbus_call(sbus, name, path, iface, "ping", "{'toto':[1,2,3,4,true,'toto']}", onresp, "ping");
1030                 sbus_call(sbus, name, path, iface, "incr", "{'doit':'for-me'}", onresp, "incr");
1031                 upoll_wait(1);
1032         }
1033         printf("[[[%s]]]\n", sbus_call_sync(sbus, name, path, iface, "ping", "formidable!"));
1034         while (1) upoll_wait(-1);
1035 }
1036 #endif
1037 #endif