4 author: José Bollo <jose.bollo@iot.bzh>
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
10 http://www.apache.org/licenses/LICENSE-2.0
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.
27 #include <dbus/dbus.h>
29 #include "utils-sbus.h"
32 * errors messages generated by sbus
34 static const char invalid_request_string[] = "invalid request";
35 static const char out_of_memory_string[] = "out of memory";
38 * structure for handled messages
41 DBusMessage *message; /* message of the message */
42 DBusConnection *connection; /* connection of the message */
46 * structure for services
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 *);
56 void *closure; /* closure for the callbacks */
60 * structure for signals
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 *);
70 void *closure; /* closure for the callbacks */
74 * structure for asynchronous requests (resp-onse w-aiter)
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 *);
85 * structure for synchronous calls
88 int replied; /* boolean flag indicating reply */
89 int status; /* received status */
90 char *value; /* copy of the returned value */
94 * structure for handling either client or server sbus on dbus
97 int refcount; /* referenced how many time */
98 DBusConnection *connection; /* connection to DBU */
99 const struct sbus_itf *itf; /* interface to the main loop */
101 struct sbus_service *services; /* first service */
102 struct sbus_signal *signals; /* first signal */
103 struct srespw *waiters; /* first response waiter */
107 static struct sbus system_sbus;
108 static struct sbus session_sbus;
110 /*********************** STATIC COMMON METHODS *****************/
113 * Frees the ressources attached to a message
115 static inline void free_sbusmsg(struct sbusmsg *smsg)
117 dbus_message_unref(smsg->message);
118 dbus_connection_unref(smsg->connection);
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
128 static inline int reply_out_of_memory(struct sbusmsg *smsg)
130 sbus_reply_error(smsg, out_of_memory_string);
136 * Checks if the incoming 'message' matches the interface
139 * Returns 1 if it matches or 0 wether it does not matches.
142 static int matchitf(struct sbus *sbus, DBusMessage * message)
144 const char *itf = dbus_message_get_interface(message);
145 return itf != NULL && !strcmp(itf, sbus->name);
150 * Callback function for synchronous calls.
151 * This function fills the respsync structure pointed by 'data'
152 * with the copy of the answer.
154 static void sync_of_replies(int status, const char *value, struct respsync *s)
157 s->value = status ? NULL : strdup(value ? value : "");
162 * Creates and returns the rule for 'signal'.
164 static char *rule_of_signal(struct sbus_signal *signal)
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 : ""
180 /*********************** STATIC DBUS MESSAGE HANDLING *****************/
183 * Handles incomming responses 'message' on 'sbus'. Response are
184 * either expected if 'iserror' == 0 or errors if 'iserror' != 0.
186 * Returns DBUS_HANDLER_RESULT_HANDLED or DBUS_HANDLER_RESULT_NOT_YET_HANDLED
187 * as defined by the dbus function 'dbus_connection_add_filter'.
189 static DBusHandlerResult incoming_resp(
191 DBusMessage * message,
196 struct srespw *jrw, **prv;
197 dbus_uint32_t serial;
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)
207 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
213 /* retrieve the string value */
214 if (dbus_message_get_args
215 (message, NULL, DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID))
222 jrw->onresp(iserror ? -1 : status, str, jrw->closure);
225 return DBUS_HANDLER_RESULT_HANDLED;
229 * Handles incomming on 'sbus' method calls for 'message'.
231 * Returns DBUS_HANDLER_RESULT_HANDLED or DBUS_HANDLER_RESULT_NOT_YET_HANDLED
232 * as defined by the dbus function 'dbus_connection_add_filter'.
234 static DBusHandlerResult incoming_call(
236 DBusMessage * message)
238 struct sbus_service *service;
239 struct sbusmsg *smsg;
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))))
250 service = service->next;
252 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
255 /* creates and init the smsg structure */
256 smsg = malloc(sizeof *smsg);
258 return DBUS_HANDLER_RESULT_NEED_MEMORY;
259 smsg->message = dbus_message_ref(message);
260 smsg->connection = dbus_connection_ref(sbus->connection);
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;
267 /* handling strings only */
268 service->oncall(smsg, str, service->closure);
269 return DBUS_HANDLER_RESULT_HANDLED;
272 sbus_reply_error(smsg, invalid_request_string);
273 return DBUS_HANDLER_RESULT_HANDLED;
277 * Handles incomming on 'sbus' signal propagated with 'message'.
279 * This is a design choice to ignore invalid signals.
281 * Returns DBUS_HANDLER_RESULT_HANDLED or DBUS_HANDLER_RESULT_NOT_YET_HANDLED
282 * as defined by the dbus function 'dbus_connection_add_filter'.
284 static DBusHandlerResult incoming_signal(
286 DBusMessage * message)
288 DBusHandlerResult result;
289 struct sbus_signal *signal;
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))
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;
310 signal = signal->next;
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.
321 static DBusHandlerResult incoming(
322 DBusConnection * connection,
323 DBusMessage * message,
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);
336 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
339 /*********************** STATIC DBUS WATCH/POLLING INTERFACE **********/
342 DBusConnection *connection;
347 static void on_hangup(struct swatch *s)
349 dbus_watch_handle(s->watch, DBUS_WATCH_HANGUP);
350 while (dbus_connection_dispatch(s->connection) == DBUS_DISPATCH_DATA_REMAINS);
353 static void on_readable(struct swatch *s)
355 dbus_watch_handle(s->watch, DBUS_WATCH_READABLE);
356 while (dbus_connection_dispatch(s->connection) == DBUS_DISPATCH_DATA_REMAINS);
359 static void on_writable(struct swatch *s)
361 dbus_watch_handle(s->watch, DBUS_WATCH_WRITABLE);
362 while (dbus_connection_dispatch(s->connection) == DBUS_DISPATCH_DATA_REMAINS);
366 * DBUS Callback for removing a 'watch'.
367 * See function 'dbus_connection_set_watch_functions'
369 static void watchdel(DBusWatch *watch, struct sbus *sbus)
371 struct swatch *s = dbus_watch_get_data(watch);
372 sbus->itf->close(s->hndl);
377 * DBUS Callback for changing a 'watch'.
378 * See function 'dbus_connection_set_watch_functions'
380 static void watchtoggle(DBusWatch *watch, struct sbus *sbus)
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);
393 * DBUS Callback for adding a 'watch'.
394 * See function 'dbus_connection_set_watch_functions'
396 static dbus_bool_t watchadd(DBusWatch *watch, struct sbus *sbus)
401 s = malloc(sizeof *s);
404 fd = dbus_watch_get_unix_fd(watch);
405 s->hndl = sbus->itf->open(fd, s, sbus->itfclo);
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);
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.
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
435 * Returns the created sbus or NULL in case of error.
437 static struct sbus *get_sbus(const struct sbus_itf *itf, void *itfclo, struct sbus *sbus)
439 /* create the sbus object */
440 if (sbus->refcount > 0) {
441 if (itf != sbus->itf)
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)
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))
464 dbus_connection_unref(sbus->connection);
465 sbus->connection = NULL;
470 /********************* MAIN FUNCTIONS *****************************************/
473 * Creates a 'sbus' bound to DBUS system using 'path' and returns it.
476 struct sbus *sbus_system(const struct sbus_itf *itf, void *itfclo)
478 return get_sbus(itf, itfclo, &system_sbus);
482 * Creates a 'sbus' bound to DBUS session using 'path' and returns it.
485 struct sbus *sbus_session(const struct sbus_itf *itf, void *itfclo)
487 return get_sbus(itf, itfclo, &session_sbus);
491 * Adds one reference to 'sbus'.
493 void sbus_addref(struct sbus *sbus)
499 * Removes one reference to 'sbus'. Destroys 'sbus' and it related
500 * data if the count of references decrease to zero.
502 void sbus_unref(struct sbus *sbus)
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;
514 while ((w = sbus->waiters)) {
515 sbus->waiters = w->next;
517 w->onresp(-1, "cancelled", w->closure);
524 * Sends from 'sbus' the signal of 'member' handling the string 'content'.
526 * Returns 0 in case of success or -1 in case of error.
528 int sbus_send_signal(struct sbus *sbus, const char *sender, const char *path, const char *iface, const char *member, const char *content)
531 DBusMessage *message;
533 message = dbus_message_new_signal(path, iface, member);
537 if (sender != NULL && !dbus_message_set_sender(message, sender))
540 if (!dbus_message_append_args(message, DBUS_TYPE_STRING, &content,
544 if (dbus_connection_send(sbus->connection, message, NULL))
547 dbus_message_unref(message);
551 dbus_message_unref(message);
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'
567 * Returns 0 in case of success or -1 in case of error.
571 const char *destination,
576 void (*onresp) (int, const char *, void *),
582 /* allocates the response structure */
583 resp = malloc(sizeof *resp);
589 /* creates the message */
590 msg = dbus_message_new_method_call(destination, path, iface, method);
597 if (!dbus_message_append_args
598 (msg, DBUS_TYPE_STRING, &query, DBUS_TYPE_INVALID)) {
604 if (!dbus_connection_send(sbus->connection, msg, &resp->serial)) {
605 /* TODO: which error? */
609 /* release the message that is not more used */
610 dbus_message_unref(msg);
612 /* fulfill the response structure */
613 resp->closure = closure;
614 resp->onresp = onresp;
616 /* links the response to list of reponse waiters */
617 resp->next = sbus->waiters;
618 sbus->waiters = resp;
622 dbus_message_unref(msg);
630 * Synchronous call to 'method' of 'sbus' passing the string 'query'.
631 * The returned string response is returned.
633 * Returns the string response or NULL in case of error.
635 char *sbus_call_sync(
637 const char *destination,
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)
651 return synchro.value;
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.
662 * Returns 0 in case of success or -1 otherwise.
664 struct sbus_signal *sbus_add_signal(
670 void (*onsignal) (const struct sbusmsg *, const char *, void *),
674 struct sbus_signal *signal;
677 signal = calloc(1, sizeof *signal);
680 if (sender != NULL) {
681 signal->sender = strdup(sender);
686 signal->path = strdup(path);
691 signal->iface = strdup(iface);
695 if (member != NULL) {
696 signal->member = strdup(member);
701 /* record the signal */
702 rule = rule_of_signal(signal);
705 dbus_bus_add_match(sbus->connection, rule, NULL);
708 /* record the signal */
709 signal->onsignal = onsignal;
710 signal->closure = closure;
711 signal->next = sbus->signals;
712 sbus->signals = signal;
717 free(signal->sender);
720 free(signal->member);
728 * Removes the 'signal' handler from 'sbus'
729 * Returns 0 in case of success or -1 in case of error.
731 int sbus_remove_signal(struct sbus *sbus, struct sbus_signal *signal)
734 struct sbus_signal **it;
737 while (*it != NULL) {
746 rule = rule_of_signal(signal);
748 dbus_bus_remove_match(sbus->connection, rule, NULL);
752 free(signal->sender);
755 free(signal->member);
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
765 * It also allows 'sbus' to emit signals of that origin.
767 * Returns 0 in case of success or -1 in case of error.
769 int sbus_add_name(struct sbus *sbus, const char *name)
771 int status = dbus_bus_request_name(sbus->connection, name,
772 DBUS_NAME_FLAG_DO_NOT_QUEUE, NULL);
774 case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
775 case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
777 case DBUS_REQUEST_NAME_REPLY_EXISTS:
778 case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
786 * Adds to 'sbus' a service handling calls to the 'method' using
787 * the callback 'oncall' and the closure value 'closure'.
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
795 * Returns 0 in case of success or -1 in case of error.
797 struct sbus_service *sbus_add_service(
799 const char *destination,
803 void (*oncall) (struct sbusmsg *, const char *, void *),
806 struct sbus_service *service;
809 service = calloc(1, sizeof *service);
812 if (destination != NULL) {
813 service->destination = strdup(destination);
814 if (!service->destination)
818 service->path = strdup(path);
823 service->iface = strdup(iface);
827 if (member != NULL) {
828 service->member = strdup(member);
829 if (!service->member)
833 /* record the service */
834 service->oncall = oncall;
835 service->closure = closure;
836 service->next = sbus->services;
837 sbus->services = service;
842 free(service->destination);
844 free(service->iface);
845 free(service->member);
853 * Removes the 'service' handler from 'sbus'
854 * Returns 0 in case of success or -1 in case of error.
856 int sbus_remove_service(struct sbus *sbus, struct sbus_service *service)
858 struct sbus_service **it;
860 it = &sbus->services;
861 while (*it != NULL) {
871 free(service->destination);
873 free(service->iface);
874 free(service->member);
879 const char *sbus_sender(const struct sbusmsg *smsg)
881 return dbus_message_get_sender(smsg->message);
884 const char *sbus_destination(const struct sbusmsg *smsg)
886 return dbus_message_get_destination(smsg->message);
889 const char *sbus_path(const struct sbusmsg *smsg)
891 return dbus_message_get_path(smsg->message);
894 const char *sbus_interface(const struct sbusmsg *smsg)
896 return dbus_message_get_interface(smsg->message);
899 const char *sbus_member(const struct sbusmsg *smsg)
901 return dbus_message_get_member(smsg->message);
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.
908 * Returns 0 in case of success or -1 in case of error.
910 int sbus_reply_error(struct sbusmsg *smsg, const char *error)
913 DBusMessage *message;
915 message = dbus_message_new_error(smsg->message, DBUS_ERROR_FAILED,
920 if (dbus_connection_send(smsg->connection, message, NULL))
922 dbus_message_unref(message);
929 * Replies normally the string 'reply' to the request handled by 'smsg'.
930 * Also destroys the request 'smsg' that must not be used later.
932 * Returns 0 in case of success or -1 in case of error.
934 int sbus_reply(struct sbusmsg *smsg, const char *reply)
937 DBusMessage *message;
939 message = dbus_message_new_method_return(smsg->message);
941 return reply_out_of_memory(smsg);
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);
949 if (dbus_connection_send(smsg->connection, message, NULL))
951 dbus_message_unref(message);
956 /****************** FEW LITTLE TESTS *****************************************/
958 #if defined(SERVER)||defined(CLIENT)
961 #include "utils-upoll.h"
963 static int mwait(int timeout, void *closure)
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
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;
984 void ping(struct sbusmsg *smsg, const char *request, void *unused)
986 printf("ping(%s) -> %s\n", request, request);
987 sbus_reply(smsg, request);
990 void incr(struct sbusmsg *smsg, const char *request, void *unused)
992 static int counter = 0;
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);
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);
1013 void onresp(int status, const char *response, void *closure)
1015 printf("resp: %d, %s, %s\n", status, (const char *)closure, response);
1018 void signaled(const struct sbusmsg *req, const char *data, void *closure)
1020 printf("signaled with {%s}/%s\n", data, (const char*)closure);
1026 sbus = sbus_session(&uitf, NULL);
1027 sbus_add_signal(sbus, name, path, iface, "incremented", signaled, "signal");
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");
1033 printf("[[[%s]]]\n", sbus_call_sync(sbus, name, path, iface, "ping", "formidable!"));
1034 while (1) upoll_wait(-1);