Quit when requested from the menu
[ladish.git] / dbus / method.c
blob5e421ac33150184ff7fffbc3364acf0ae001e23c
1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
2 /*
3 * LADI Session Handler (ladish)
5 * Copyright (C) 2008 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"
35 * Construct a void method return.
37 * The operation can only fail due to lack of memory, in which case
38 * there's no sense in trying to construct an error return. Instead,
39 * call->reply will be set to NULL and handled in send_method_return().
41 void
42 method_return_new_void(method_call_t *call)
44 if (!(call->reply = dbus_message_new_method_return(call->message))) {
45 lash_error("Ran out of memory trying to construct method return");
50 * Construct a method return which holds a single argument or, if
51 * the type parameter is DBUS_TYPE_INVALID, no arguments at all
52 * (a void message).
54 * The operation can only fail due to lack of memory, in which case
55 * there's no sense in trying to construct an error return. Instead,
56 * call->reply will be set to NULL and handled in send_method_return().
58 void
59 method_return_new_single(method_call_t *call,
60 int type,
61 const void *arg)
63 if (!call || !arg) {
64 lash_error("Invalid arguments");
65 return;
68 call->reply = dbus_message_new_method_return(call->message);
70 if (!call->reply)
71 goto fail_no_mem;
73 /* Void method return requested by caller. */
74 // TODO: do we really need this?
75 if (type == DBUS_TYPE_INVALID)
76 return;
78 /* Prevent crash on NULL input string. */
79 if (type == DBUS_TYPE_STRING && !(*((const char **) arg)))
80 *((const char **) arg) = "";
82 DBusMessageIter iter;
84 dbus_message_iter_init_append(call->reply, &iter);
86 if (dbus_message_iter_append_basic(&iter, type, arg))
87 return;
89 dbus_message_unref(call->reply);
90 call->reply = NULL;
92 fail_no_mem:
93 lash_error("Ran out of memory trying to construct method return");
96 void
97 method_return_new_valist(method_call_t *call,
98 int type,
99 ...)
101 if (!call) {
102 lash_error("Call pointer is NULL");
103 return;
106 if (type == DBUS_TYPE_INVALID) {
107 lash_error("No argument(s) supplied");
108 return;
111 va_list argp;
113 call->reply = dbus_message_new_method_return(call->message);
114 if (!call->reply)
115 goto fail_no_mem;
117 va_start(argp, type);
119 if (dbus_message_append_args_valist(call->reply, type, argp)) {
120 va_end(argp);
121 return;
124 va_end(argp);
126 dbus_message_unref(call->reply);
127 call->reply = NULL;
129 fail_no_mem:
130 lash_error("Ran out of memory trying to construct method return");
134 * Send a method call.
136 bool
137 method_send(method_msg_t *call,
138 bool will_block)
140 if (!call->message) {
141 lash_error("Cannot send method call: Message is NULL");
142 return false;
145 DBusPendingCall *pending = NULL;
146 bool retval;
148 if (!dbus_connection_send_with_reply(call->service->connection,
149 call->message, &pending, -1)) {
150 lash_error("Ran out of memory trying to queue "
151 "method call");
152 retval = false;
153 /* Never wait for a reply if sending failed */
154 will_block = false;
155 } else {
156 if (pending)
157 dbus_pending_call_set_notify(pending,
158 call->return_handler,
159 call->context, NULL);
160 dbus_connection_flush(call->service->connection);
161 retval = true;
164 dbus_message_unref(call->message);
165 call->message = NULL;
167 if (will_block && pending) {
168 lash_debug("Blocking until a return arrives");
169 dbus_pending_call_block(pending);
172 return retval;
176 * Send a method return.
178 * If call->reply is NULL, i.e. a previous attempt to construct
179 * a return has failed, attempt to send a void return.
181 void
182 method_return_send(method_call_t *call)
184 if (call->reply) {
185 retry_send:
186 if (!dbus_connection_send(call->connection, call->reply, NULL))
187 lash_error("Ran out of memory trying to queue "
188 "method return");
189 else
190 dbus_connection_flush(call->connection);
192 dbus_message_unref(call->reply);
193 call->reply = NULL;
194 } else {
195 lash_debug("Message was NULL, trying to construct a void return");
197 if ((call->reply = dbus_message_new_method_return(call->message))) {
198 lash_debug("Constructed a void return, trying to queue it");
199 goto retry_send;
200 } else {
201 lash_error("Failed to construct method return!");
206 bool
207 method_return_verify(DBusMessage *msg,
208 const char **str)
210 if (!msg || dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_ERROR)
211 return true;
213 DBusError err;
214 const char *ptr;
216 dbus_error_init(&err);
218 if (!dbus_message_get_args(msg, &err,
219 DBUS_TYPE_STRING, &ptr,
220 DBUS_TYPE_INVALID)) {
221 lash_error("Cannot read description from D-Bus error message: %s ",
222 err.message);
223 dbus_error_free(&err);
224 ptr = NULL;
227 if (str)
228 *str = ptr;
230 return false;
234 * Append a variant type to a D-Bus message.
235 * Return false if something fails, true otherwise.
237 bool
238 method_iter_append_variant(DBusMessageIter *iter,
239 int type,
240 const void *arg)
242 DBusMessageIter sub_iter;
243 char s[2];
245 s[0] = (char) type;
246 s[1] = '\0';
248 /* Open a variant container. */
249 if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
250 (const char *) s, &sub_iter))
251 return false;
253 /* Append the supplied value. */
254 if (!dbus_message_iter_append_basic(&sub_iter, type, arg)) {
255 dbus_message_iter_close_container(iter, &sub_iter);
256 return false;
259 /* Close the container. */
260 if (!dbus_message_iter_close_container(iter, &sub_iter))
261 return false;
263 return true;
266 static __inline__ bool
267 method_iter_append_variant_raw(DBusMessageIter *iter,
268 const void *buf,
269 int len)
271 DBusMessageIter variant_iter, array_iter;
273 /* Open a variant container. */
274 if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
275 "ay", &variant_iter))
276 return false;
278 /* Open an array container. */
279 if (!dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
280 "y", &array_iter))
281 goto fail;
283 /* Append the supplied data. */
284 if (!dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE, buf, len)) {
285 dbus_message_iter_close_container(&variant_iter, &array_iter);
286 goto fail;
289 /* Close the containers. */
290 if (!dbus_message_iter_close_container(&variant_iter, &array_iter))
291 goto fail;
292 else if (!dbus_message_iter_close_container(iter, &variant_iter))
293 return false;
295 return true;
297 fail:
298 dbus_message_iter_close_container(iter, &variant_iter);
299 return false;
302 bool
303 method_iter_append_dict_entry(DBusMessageIter *iter,
304 int type,
305 const char *key,
306 const void *value,
307 int length)
309 DBusMessageIter dict_iter;
311 if (!dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY,
312 NULL, &dict_iter))
313 return false;
315 if (!dbus_message_iter_append_basic(&dict_iter, DBUS_TYPE_STRING, &key))
316 goto fail;
318 if (type == '-') {
319 if (!method_iter_append_variant_raw(&dict_iter, value, length))
320 goto fail;
321 } else if (!method_iter_append_variant(&dict_iter, type, value))
322 goto fail;
324 if (!dbus_message_iter_close_container(iter, &dict_iter))
325 return false;
327 return true;
329 fail:
330 dbus_message_iter_close_container(iter, &dict_iter);
331 return false;
334 void
335 method_default_handler(DBusPendingCall *pending,
336 void *data)
338 DBusMessage *msg = dbus_pending_call_steal_reply(pending);
340 if (msg) {
341 const char *err_str;
343 if (!method_return_verify(msg, &err_str))
344 lash_error("%s", err_str);
346 dbus_message_unref(msg);
347 } else
348 lash_error("Cannot get method return from pending call");
350 dbus_pending_call_unref(pending);
353 bool
354 method_call_init(method_msg_t *call,
355 service_t *service,
356 void *return_context,
357 DBusPendingCallNotifyFunction return_handler,
358 const char *destination,
359 const char *path,
360 const char *interface,
361 const char *method)
363 if (!call || !service || !service->connection
364 || !destination || !path || !method) {
365 lash_error("Invalid arguments");
366 return false;
369 if (!interface)
370 interface = "";
372 call->message = dbus_message_new_method_call(destination, path,
373 interface, method);
374 if (!call->message) {
375 lash_error("Ran out of memory trying to create "
376 "new method call");
377 return false;
380 call->service = service;
381 call->context = return_context;
382 call->return_handler = return_handler;
384 if (!return_handler)
385 dbus_message_set_no_reply(call->message, true);
387 return true;
390 bool
391 method_call_new_void(service_t *service,
392 void *return_context,
393 DBusPendingCallNotifyFunction return_handler,
394 bool will_block,
395 const char *destination,
396 const char *path,
397 const char *interface,
398 const char *method)
400 method_msg_t call;
402 if (method_call_init(&call, service, return_context, return_handler,
403 destination, path, interface, method))
404 return method_send(&call, will_block);
406 return false;
409 bool
410 method_call_new_single(service_t *service,
411 void *return_context,
412 DBusPendingCallNotifyFunction return_handler,
413 bool will_block,
414 const char *destination,
415 const char *path,
416 const char *interface,
417 const char *method,
418 int type,
419 const void *arg)
421 if (type == DBUS_TYPE_INVALID || !arg) {
422 lash_error("No argument supplied");
423 return false;
426 method_msg_t call;
427 DBusMessageIter iter;
429 if (!method_call_init(&call, service, return_context, return_handler,
430 destination, path, interface, method))
431 return false;
433 dbus_message_iter_init_append(call.message, &iter);
435 if (dbus_message_iter_append_basic(&iter, type, arg)) {
436 return method_send(&call, will_block);
439 lash_error("Ran out of memory trying to append method call argument");
441 dbus_message_unref(call.message);
442 call.message = NULL;
444 return false;
447 bool
448 method_call_new_valist(service_t *service,
449 void *return_context,
450 DBusPendingCallNotifyFunction return_handler,
451 bool will_block,
452 const char *destination,
453 const char *path,
454 const char *interface,
455 const char *method,
456 int type,
457 ...)
459 if (type == DBUS_TYPE_INVALID) {
460 lash_error("No argument(s) supplied");
461 return false;
464 method_msg_t call;
465 va_list argp;
467 if (!method_call_init(&call, service, return_context, return_handler,
468 destination, path, interface, method))
469 return false;
471 va_start(argp, type);
473 if (dbus_message_append_args_valist(call.message, type, argp)) {
474 va_end(argp);
475 return method_send(&call, will_block);
478 va_end(argp);
480 lash_error("Ran out of memory trying to append "
481 "method call argument(s)");
483 dbus_message_unref(call.message);
484 call.message = NULL;
486 return false;
489 bool
490 method_iter_get_args(DBusMessageIter *iter,
491 ...)
493 if (!iter) {
494 lash_error("Iterator pointer is NULL");
495 return false;
498 va_list argp;
499 int expected_type, type, n = 0;
500 void *ptr;
502 va_start(argp, iter);
504 while ((expected_type = va_arg(argp, int)) != DBUS_TYPE_INVALID) {
505 ptr = va_arg(argp, void *);
507 if ((type = dbus_message_iter_get_arg_type(iter))
508 == DBUS_TYPE_INVALID) {
509 lash_error("Insufficient arguments");
510 va_end(argp);
511 return false;
514 if (type != expected_type) {
515 lash_error("Invalid type for argument %i", n);
516 va_end(argp);
517 return false;
520 if (ptr)
521 dbus_message_iter_get_basic(iter, ptr);
523 dbus_message_iter_next(iter);
525 ++n;
528 va_end(argp);
530 return true;
533 bool
534 method_iter_get_dict_entry(DBusMessageIter *iter,
535 const char **key_ptr,
536 void *value_ptr,
537 int *type_ptr,
538 int *size_ptr)
540 if (!iter || !key_ptr || !value_ptr || !type_ptr) {
541 lash_error("Invalid arguments");
542 return false;
545 DBusMessageIter dict_iter, variant_iter;
547 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_DICT_ENTRY) {
548 lash_error("Iterator does not point to a dict entry container");
549 return false;
552 dbus_message_iter_recurse(iter, &dict_iter);
554 if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_STRING) {
555 lash_error("Cannot find key in dict entry container");
556 return false;
559 dbus_message_iter_get_basic(&dict_iter, key_ptr);
561 if (!dbus_message_iter_next(&dict_iter)
562 || dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_VARIANT) {
563 lash_error("Cannot find variant container in dict entry");
564 return false;
567 dbus_message_iter_recurse(&dict_iter, &variant_iter);
569 *type_ptr = dbus_message_iter_get_arg_type(&variant_iter);
570 if (*type_ptr == DBUS_TYPE_INVALID) {
571 lash_error("Cannot find value in variant container");
572 return false;
575 if (*type_ptr == DBUS_TYPE_ARRAY) {
576 DBusMessageIter array_iter;
577 int n;
579 if (dbus_message_iter_get_element_type(&variant_iter)
580 != DBUS_TYPE_BYTE) {
581 lash_error("Dict entry value is a non-byte array");
582 return false;
584 *type_ptr = '-';
586 dbus_message_iter_recurse(&variant_iter, &array_iter);
587 dbus_message_iter_get_fixed_array(&array_iter, value_ptr, &n);
589 if (size_ptr)
590 *size_ptr = n;
591 } else
592 dbus_message_iter_get_basic(&variant_iter, value_ptr);
594 return true;
597 /* EOF */