ladish_control: Expose all current methods
[ladish.git] / dbus / method.c
blobbbf37c7aa2f4d98524409df700b21b2815cae5f8
1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
2 /*
3 * LADI Session Handler (ladish)
5 * Copyright (C) 2008, 2009 Nedko Arnaudov <nedko@arnaudov.name>
6 * Copyright (C) 2008 Juuso Alasuutari <juuso.alasuutari@gmail.com>
8 **************************************************************************
9 * This file contains D-Bus methods helpers
10 **************************************************************************
12 * LADI Session Handler is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * LADI Session Handler is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with LADI Session Handler. If not, see <http://www.gnu.org/licenses/>
24 * or write to the Free Software Foundation, Inc.,
25 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
28 #include "../common/safety.h"
29 #include "../common/debug.h"
31 #include "method.h"
32 #include "service.h"
33 #include "helpers.h"
36 * Construct a void method return.
38 * The operation can only fail due to lack of memory, in which case
39 * there's no sense in trying to construct an error return. Instead,
40 * call->reply will be set to NULL and handled in send_method_return().
42 void
43 method_return_new_void(method_call_t *call)
45 if (!(call->reply = dbus_message_new_method_return(call->message))) {
46 lash_error("Ran out of memory trying to construct method return");
51 * Construct a method return which holds a single argument or, if
52 * the type parameter is DBUS_TYPE_INVALID, no arguments at all
53 * (a void message).
55 * The operation can only fail due to lack of memory, in which case
56 * there's no sense in trying to construct an error return. Instead,
57 * call->reply will be set to NULL and handled in send_method_return().
59 void
60 method_return_new_single(method_call_t *call,
61 int type,
62 const void *arg)
64 if (!call || !arg) {
65 lash_error("Invalid arguments");
66 return;
69 call->reply = dbus_message_new_method_return(call->message);
71 if (!call->reply)
72 goto fail_no_mem;
74 /* Void method return requested by caller. */
75 // TODO: do we really need this?
76 if (type == DBUS_TYPE_INVALID)
77 return;
79 /* Prevent crash on NULL input string. */
80 if (type == DBUS_TYPE_STRING && !(*((const char **) arg)))
81 *((const char **) arg) = "";
83 DBusMessageIter iter;
85 dbus_message_iter_init_append(call->reply, &iter);
87 if (dbus_message_iter_append_basic(&iter, type, arg))
88 return;
90 dbus_message_unref(call->reply);
91 call->reply = NULL;
93 fail_no_mem:
94 lash_error("Ran out of memory trying to construct method return");
97 void
98 method_return_new_valist(method_call_t *call,
99 int type,
100 ...)
102 if (!call) {
103 lash_error("Call pointer is NULL");
104 return;
107 if (type == DBUS_TYPE_INVALID) {
108 lash_error("No argument(s) supplied");
109 return;
112 va_list argp;
114 call->reply = dbus_message_new_method_return(call->message);
115 if (!call->reply)
116 goto fail_no_mem;
118 va_start(argp, type);
120 if (dbus_message_append_args_valist(call->reply, type, argp)) {
121 va_end(argp);
122 return;
125 va_end(argp);
127 dbus_message_unref(call->reply);
128 call->reply = NULL;
130 fail_no_mem:
131 lash_error("Ran out of memory trying to construct method return");
135 * Send a method call.
137 bool
138 method_send(method_msg_t *call,
139 bool will_block)
141 if (!call->message) {
142 lash_error("Cannot send method call: Message is NULL");
143 return false;
146 DBusPendingCall *pending = NULL;
147 bool retval;
149 if (!dbus_connection_send_with_reply(call->service->connection,
150 call->message, &pending, -1)) {
151 lash_error("Ran out of memory trying to queue "
152 "method call");
153 retval = false;
154 /* Never wait for a reply if sending failed */
155 will_block = false;
156 } else {
157 if (pending)
158 dbus_pending_call_set_notify(pending,
159 call->return_handler,
160 call->context, NULL);
161 dbus_connection_flush(call->service->connection);
162 retval = true;
165 dbus_message_unref(call->message);
166 call->message = NULL;
168 if (will_block && pending) {
169 lash_debug("Blocking until a return arrives");
170 dbus_pending_call_block(pending);
173 return retval;
177 * Send a method return.
179 * If call->reply is NULL, i.e. a previous attempt to construct
180 * a return has failed, attempt to send a void return.
182 void
183 method_return_send(method_call_t *call)
185 if (call->reply) {
186 retry_send:
187 if (!dbus_connection_send(call->connection, call->reply, NULL))
188 lash_error("Ran out of memory trying to queue "
189 "method return");
190 else
191 dbus_connection_flush(call->connection);
193 dbus_message_unref(call->reply);
194 call->reply = NULL;
195 } else {
196 lash_debug("Message was NULL, trying to construct a void return");
198 if ((call->reply = dbus_message_new_method_return(call->message))) {
199 lash_debug("Constructed a void return, trying to queue it");
200 goto retry_send;
201 } else {
202 lash_error("Failed to construct method return!");
207 bool
208 method_return_verify(DBusMessage *msg,
209 const char **str)
211 if (!msg || dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_ERROR)
212 return true;
214 const char *ptr;
216 if (!dbus_message_get_args(msg, &g_dbus_error,
217 DBUS_TYPE_STRING, &ptr,
218 DBUS_TYPE_INVALID)) {
219 lash_error("Cannot read description from D-Bus error message: %s ",
220 g_dbus_error.message);
221 dbus_error_free(&g_dbus_error);
222 ptr = NULL;
225 if (str)
226 *str = ptr;
228 return false;
232 * Append a variant type to a D-Bus message.
233 * Return false if something fails, true otherwise.
235 bool
236 method_iter_append_variant(DBusMessageIter *iter,
237 int type,
238 const void *arg)
240 DBusMessageIter sub_iter;
241 char s[2];
243 s[0] = (char) type;
244 s[1] = '\0';
246 /* Open a variant container. */
247 if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
248 (const char *) s, &sub_iter))
249 return false;
251 /* Append the supplied value. */
252 if (!dbus_message_iter_append_basic(&sub_iter, type, arg)) {
253 dbus_message_iter_close_container(iter, &sub_iter);
254 return false;
257 /* Close the container. */
258 if (!dbus_message_iter_close_container(iter, &sub_iter))
259 return false;
261 return true;
264 static __inline__ bool
265 method_iter_append_variant_raw(DBusMessageIter *iter,
266 const void *buf,
267 int len)
269 DBusMessageIter variant_iter, array_iter;
271 /* Open a variant container. */
272 if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
273 "ay", &variant_iter))
274 return false;
276 /* Open an array container. */
277 if (!dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
278 "y", &array_iter))
279 goto fail;
281 /* Append the supplied data. */
282 if (!dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE, buf, len)) {
283 dbus_message_iter_close_container(&variant_iter, &array_iter);
284 goto fail;
287 /* Close the containers. */
288 if (!dbus_message_iter_close_container(&variant_iter, &array_iter))
289 goto fail;
290 else if (!dbus_message_iter_close_container(iter, &variant_iter))
291 return false;
293 return true;
295 fail:
296 dbus_message_iter_close_container(iter, &variant_iter);
297 return false;
300 bool
301 method_iter_append_dict_entry(DBusMessageIter *iter,
302 int type,
303 const char *key,
304 const void *value,
305 int length)
307 DBusMessageIter dict_iter;
309 if (!dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY,
310 NULL, &dict_iter))
311 return false;
313 if (!dbus_message_iter_append_basic(&dict_iter, DBUS_TYPE_STRING, &key))
314 goto fail;
316 if (type == '-') {
317 if (!method_iter_append_variant_raw(&dict_iter, value, length))
318 goto fail;
319 } else if (!method_iter_append_variant(&dict_iter, type, value))
320 goto fail;
322 if (!dbus_message_iter_close_container(iter, &dict_iter))
323 return false;
325 return true;
327 fail:
328 dbus_message_iter_close_container(iter, &dict_iter);
329 return false;
332 void
333 method_default_handler(DBusPendingCall *pending,
334 void *data)
336 DBusMessage *msg = dbus_pending_call_steal_reply(pending);
338 if (msg) {
339 const char *err_str;
341 if (!method_return_verify(msg, &err_str))
342 lash_error("%s", err_str);
344 dbus_message_unref(msg);
345 } else
346 lash_error("Cannot get method return from pending call");
348 dbus_pending_call_unref(pending);
351 bool
352 method_call_init(method_msg_t *call,
353 service_t *service,
354 void *return_context,
355 DBusPendingCallNotifyFunction return_handler,
356 const char *destination,
357 const char *path,
358 const char *interface,
359 const char *method)
361 if (!call || !service || !service->connection
362 || !destination || !path || !method) {
363 lash_error("Invalid arguments");
364 return false;
367 if (!interface)
368 interface = "";
370 call->message = dbus_message_new_method_call(destination, path,
371 interface, method);
372 if (!call->message) {
373 lash_error("Ran out of memory trying to create "
374 "new method call");
375 return false;
378 call->service = service;
379 call->context = return_context;
380 call->return_handler = return_handler;
382 if (!return_handler)
383 dbus_message_set_no_reply(call->message, true);
385 return true;
388 bool
389 method_call_new_void(service_t *service,
390 void *return_context,
391 DBusPendingCallNotifyFunction return_handler,
392 bool will_block,
393 const char *destination,
394 const char *path,
395 const char *interface,
396 const char *method)
398 method_msg_t call;
400 if (method_call_init(&call, service, return_context, return_handler,
401 destination, path, interface, method))
402 return method_send(&call, will_block);
404 return false;
407 bool
408 method_call_new_single(service_t *service,
409 void *return_context,
410 DBusPendingCallNotifyFunction return_handler,
411 bool will_block,
412 const char *destination,
413 const char *path,
414 const char *interface,
415 const char *method,
416 int type,
417 const void *arg)
419 if (type == DBUS_TYPE_INVALID || !arg) {
420 lash_error("No argument supplied");
421 return false;
424 method_msg_t call;
425 DBusMessageIter iter;
427 if (!method_call_init(&call, service, return_context, return_handler,
428 destination, path, interface, method))
429 return false;
431 dbus_message_iter_init_append(call.message, &iter);
433 if (dbus_message_iter_append_basic(&iter, type, arg)) {
434 return method_send(&call, will_block);
437 lash_error("Ran out of memory trying to append method call argument");
439 dbus_message_unref(call.message);
440 call.message = NULL;
442 return false;
445 bool
446 method_call_new_valist(service_t *service,
447 void *return_context,
448 DBusPendingCallNotifyFunction return_handler,
449 bool will_block,
450 const char *destination,
451 const char *path,
452 const char *interface,
453 const char *method,
454 int type,
455 ...)
457 if (type == DBUS_TYPE_INVALID) {
458 lash_error("No argument(s) supplied");
459 return false;
462 method_msg_t call;
463 va_list argp;
465 if (!method_call_init(&call, service, return_context, return_handler,
466 destination, path, interface, method))
467 return false;
469 va_start(argp, type);
471 if (dbus_message_append_args_valist(call.message, type, argp)) {
472 va_end(argp);
473 return method_send(&call, will_block);
476 va_end(argp);
478 lash_error("Ran out of memory trying to append "
479 "method call argument(s)");
481 dbus_message_unref(call.message);
482 call.message = NULL;
484 return false;
487 bool
488 method_iter_get_args(DBusMessageIter *iter,
489 ...)
491 if (!iter) {
492 lash_error("Iterator pointer is NULL");
493 return false;
496 va_list argp;
497 int expected_type, type, n = 0;
498 void *ptr;
500 va_start(argp, iter);
502 while ((expected_type = va_arg(argp, int)) != DBUS_TYPE_INVALID) {
503 ptr = va_arg(argp, void *);
505 if ((type = dbus_message_iter_get_arg_type(iter))
506 == DBUS_TYPE_INVALID) {
507 lash_error("Insufficient arguments");
508 va_end(argp);
509 return false;
512 if (type != expected_type) {
513 lash_error("Invalid type for argument %i", n);
514 va_end(argp);
515 return false;
518 if (ptr)
519 dbus_message_iter_get_basic(iter, ptr);
521 dbus_message_iter_next(iter);
523 ++n;
526 va_end(argp);
528 return true;
531 bool
532 method_iter_get_dict_entry(DBusMessageIter *iter,
533 const char **key_ptr,
534 void *value_ptr,
535 int *type_ptr,
536 int *size_ptr)
538 if (!iter || !key_ptr || !value_ptr || !type_ptr) {
539 lash_error("Invalid arguments");
540 return false;
543 DBusMessageIter dict_iter, variant_iter;
545 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_DICT_ENTRY) {
546 lash_error("Iterator does not point to a dict entry container");
547 return false;
550 dbus_message_iter_recurse(iter, &dict_iter);
552 if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_STRING) {
553 lash_error("Cannot find key in dict entry container");
554 return false;
557 dbus_message_iter_get_basic(&dict_iter, key_ptr);
559 if (!dbus_message_iter_next(&dict_iter)
560 || dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_VARIANT) {
561 lash_error("Cannot find variant container in dict entry");
562 return false;
565 dbus_message_iter_recurse(&dict_iter, &variant_iter);
567 *type_ptr = dbus_message_iter_get_arg_type(&variant_iter);
568 if (*type_ptr == DBUS_TYPE_INVALID) {
569 lash_error("Cannot find value in variant container");
570 return false;
573 if (*type_ptr == DBUS_TYPE_ARRAY) {
574 DBusMessageIter array_iter;
575 int n;
577 if (dbus_message_iter_get_element_type(&variant_iter)
578 != DBUS_TYPE_BYTE) {
579 lash_error("Dict entry value is a non-byte array");
580 return false;
582 *type_ptr = '-';
584 dbus_message_iter_recurse(&variant_iter, &array_iter);
585 dbus_message_iter_get_fixed_array(&array_iter, value_ptr, &n);
587 if (size_ptr)
588 *size_ptr = n;
589 } else
590 dbus_message_iter_get_basic(&variant_iter, value_ptr);
592 return true;
595 /* EOF */