406 lines
9.9 KiB
C
406 lines
9.9 KiB
C
/* -*- Mode: C ; c-basic-offset: 2 -*- */
|
|
/*
|
|
* ALSA SEQ < - > JACK MIDI bridge
|
|
*
|
|
* Copyright (c) 2007,2008,2009 Nedko Arnaudov <nedko@arnaudov.name>
|
|
* Copyright (C) 2007-2008 Juuso Alasuutari
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; version 2 of the License.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <dbus/dbus.h>
|
|
|
|
#include "dbus.h"
|
|
#include "log.h"
|
|
#include "dbus_internal.h"
|
|
#include "dbus_iface_control.h"
|
|
|
|
void
|
|
a2j_dbus_error(
|
|
void *dbus_call_context_ptr,
|
|
const char *error_name,
|
|
const char *format,
|
|
...)
|
|
{
|
|
va_list ap;
|
|
char buffer[300];
|
|
|
|
va_start(ap, format);
|
|
|
|
vsnprintf(buffer, sizeof(buffer), format, ap);
|
|
|
|
a2j_error("%s", buffer);
|
|
if (dbus_call_context_ptr != NULL)
|
|
{
|
|
((struct a2j_dbus_method_call *)dbus_call_context_ptr)->reply = dbus_message_new_error(
|
|
((struct a2j_dbus_method_call *)dbus_call_context_ptr)->message,
|
|
error_name,
|
|
buffer);
|
|
}
|
|
|
|
va_end(ap);
|
|
}
|
|
|
|
/*
|
|
* Send a method return.
|
|
*
|
|
* If call->reply is NULL (i.e. a message construct method failed
|
|
* due to lack of memory) attempt to send a void method return.
|
|
*/
|
|
static
|
|
void
|
|
a2j_dbus_send_method_return(
|
|
struct a2j_dbus_method_call * call)
|
|
{
|
|
if (call->reply == NULL)
|
|
{
|
|
a2j_debug("send_method_return() called with a NULL message, trying to construct a void return...");
|
|
|
|
call->reply = dbus_message_new_method_return(call->message);
|
|
if (call->reply == NULL)
|
|
{
|
|
a2j_error("Failed to construct method return!");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!dbus_connection_send(call->connection, call->reply, NULL))
|
|
{
|
|
a2j_error("Ran out of memory trying to queue method return");
|
|
}
|
|
|
|
dbus_connection_flush(call->connection);
|
|
dbus_message_unref(call->reply);
|
|
call->reply = NULL;
|
|
}
|
|
|
|
/*
|
|
* Construct a method return which holds a single argument or, if
|
|
* the type parameter is DBUS_TYPE_INVALID, no arguments at all
|
|
* (a void message).
|
|
*
|
|
* The operation can only fail due to lack of memory, in which case
|
|
* there's no sense in trying to construct an error return. Instead,
|
|
* call->reply will be set to NULL and handled in send_method_return().
|
|
*/
|
|
void
|
|
a2j_dbus_construct_method_return_single(
|
|
struct a2j_dbus_method_call * call_ptr,
|
|
int type,
|
|
void * arg)
|
|
{
|
|
DBusMessageIter iter;
|
|
|
|
call_ptr->reply = dbus_message_new_method_return(call_ptr->message);
|
|
if (call_ptr->reply == NULL)
|
|
{
|
|
goto fail_no_mem;
|
|
}
|
|
|
|
dbus_message_iter_init_append(call_ptr->reply, &iter);
|
|
|
|
if (!dbus_message_iter_append_basic(&iter, type, arg))
|
|
{
|
|
dbus_message_unref(call_ptr->reply);
|
|
call_ptr->reply = NULL;
|
|
goto fail_no_mem;
|
|
}
|
|
|
|
return;
|
|
|
|
fail_no_mem:
|
|
a2j_error("Ran out of memory trying to construct method return");
|
|
}
|
|
|
|
void a2j_dbus_construct_method_return_void(struct a2j_dbus_method_call * call_ptr)
|
|
{
|
|
call_ptr->reply = dbus_message_new_method_return(call_ptr->message);
|
|
if (call_ptr->reply == NULL)
|
|
{
|
|
a2j_error("Ran out of memory trying to construct method return");
|
|
}
|
|
}
|
|
|
|
#define descriptor_ptr ((struct a2j_dbus_object_descriptor *)data)
|
|
|
|
DBusHandlerResult
|
|
a2j_dbus_message_handler(
|
|
DBusConnection * connection,
|
|
DBusMessage * message,
|
|
void * data)
|
|
{
|
|
struct a2j_dbus_method_call call;
|
|
const char *interface_name;
|
|
struct a2j_dbus_interface_descriptor ** interface_ptr_ptr;
|
|
|
|
/* Check if the message is a method call. If not, ignore it. */
|
|
if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
|
|
{
|
|
goto handled;
|
|
}
|
|
|
|
/* Get the invoked method's name and make sure it's non-NULL. */
|
|
if (!(call.method_name = dbus_message_get_member (message)))
|
|
{
|
|
a2j_dbus_error(
|
|
&call,
|
|
A2J_DBUS_ERROR_UNKNOWN_METHOD,
|
|
"Received method call with empty method name");
|
|
goto send_return;
|
|
}
|
|
|
|
/* Initialize our data. */
|
|
call.context = descriptor_ptr->context;
|
|
call.connection = connection;
|
|
call.message = message;
|
|
call.reply = NULL;
|
|
|
|
/* Check if there's an interface specified for this method call. */
|
|
interface_name = dbus_message_get_interface (message);
|
|
if (interface_name != NULL)
|
|
{
|
|
/* Check if we can match the interface and method.
|
|
* The inteface handler functions only return false if the
|
|
* method name was unknown, otherwise they run the specified
|
|
* method and return TRUE.
|
|
*/
|
|
|
|
interface_ptr_ptr = descriptor_ptr->interfaces;
|
|
|
|
while (*interface_ptr_ptr != NULL)
|
|
{
|
|
if (strcmp(interface_name, (*interface_ptr_ptr)->name) == 0)
|
|
{
|
|
if (!(*interface_ptr_ptr)->handler(&call, (*interface_ptr_ptr)->methods))
|
|
{
|
|
break;
|
|
}
|
|
|
|
goto send_return;
|
|
}
|
|
|
|
interface_ptr_ptr++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* No interface was specified so we have to try them all. This is
|
|
* dictated by the D-Bus specification which states that method calls
|
|
* omitting the interface must never be rejected.
|
|
*/
|
|
|
|
interface_ptr_ptr = descriptor_ptr->interfaces;
|
|
|
|
while (*interface_ptr_ptr != NULL)
|
|
{
|
|
if ((*interface_ptr_ptr)->handler(&call, (*interface_ptr_ptr)->methods))
|
|
{
|
|
goto send_return;
|
|
}
|
|
|
|
interface_ptr_ptr++;
|
|
}
|
|
}
|
|
|
|
a2j_dbus_error(
|
|
&call,
|
|
A2J_DBUS_ERROR_UNKNOWN_METHOD,
|
|
"Method \"%s\" with signature \"%s\" on interface \"%s\" doesn't exist",
|
|
call.method_name,
|
|
dbus_message_get_signature(message),
|
|
interface_name);
|
|
|
|
send_return:
|
|
a2j_dbus_send_method_return(&call);
|
|
|
|
handled:
|
|
return DBUS_HANDLER_RESULT_HANDLED;
|
|
}
|
|
|
|
void
|
|
a2j_dbus_message_handler_unregister(
|
|
DBusConnection *connection,
|
|
void *data)
|
|
{
|
|
((void)(connection)); /* unreferenced parameter */
|
|
((void)(data)); /* unreferenced parameter */
|
|
a2j_debug("Message handler was unregistered");
|
|
}
|
|
|
|
#undef descriptor_ptr
|
|
|
|
/*
|
|
* Check if the supplied method name exists in method descriptor,
|
|
* if it does execute it and return TRUE. Otherwise return FALSE.
|
|
*/
|
|
bool
|
|
a2j_dbus_run_method(
|
|
struct a2j_dbus_method_call *call,
|
|
const struct a2j_dbus_interface_method_descriptor * methods)
|
|
{
|
|
const struct a2j_dbus_interface_method_descriptor * method_ptr;
|
|
|
|
method_ptr = methods;
|
|
|
|
while (method_ptr->name != NULL)
|
|
{
|
|
if (strcmp(call->method_name, method_ptr->name) == 0)
|
|
{
|
|
method_ptr->handler(call);
|
|
return TRUE;
|
|
}
|
|
|
|
method_ptr++;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static DBusConnection * g_dbus_connection_ptr;
|
|
static struct a2j_dbus_object_descriptor g_a2j_dbus_object_descriptor;
|
|
struct a2j_dbus_interface_descriptor * g_a2j_dbus_interfaces[] =
|
|
{
|
|
&g_a2j_iface_introspectable,
|
|
&g_a2j_iface_control,
|
|
NULL
|
|
};
|
|
|
|
bool
|
|
a2j_dbus_is_available(void)
|
|
{
|
|
return g_dbus_connection_ptr != NULL;
|
|
}
|
|
|
|
bool
|
|
a2j_dbus_init(void)
|
|
{
|
|
DBusError dbus_error;
|
|
int ret;
|
|
DBusObjectPathVTable vtable = {
|
|
.unregister_function = a2j_dbus_message_handler_unregister,
|
|
.message_function = a2j_dbus_message_handler
|
|
};
|
|
|
|
dbus_error_init(&dbus_error);
|
|
g_dbus_connection_ptr = dbus_bus_get(DBUS_BUS_SESSION, &dbus_error);
|
|
if (dbus_error_is_set(&dbus_error))
|
|
{
|
|
a2j_error("Failed to get bus: %s", dbus_error.message);
|
|
goto fail;
|
|
}
|
|
|
|
dbus_connection_set_exit_on_disconnect(g_dbus_connection_ptr, FALSE);
|
|
|
|
a2j_debug("D-Bus unique name is '%s'", dbus_bus_get_unique_name(g_dbus_connection_ptr));
|
|
|
|
ret = dbus_bus_request_name(g_dbus_connection_ptr, A2J_DBUS_SERVICE_NAME, DBUS_NAME_FLAG_DO_NOT_QUEUE, &dbus_error);
|
|
if (ret == -1)
|
|
{
|
|
a2j_error("Failed to acquire bus name: %s", dbus_error.message);
|
|
goto fail_unref_dbus_connection;
|
|
}
|
|
|
|
if (ret == DBUS_REQUEST_NAME_REPLY_EXISTS)
|
|
{
|
|
a2j_error("Requested bus name already exists");
|
|
goto fail_unref_dbus_connection;
|
|
}
|
|
|
|
g_a2j_dbus_object_descriptor.context = NULL;
|
|
g_a2j_dbus_object_descriptor.interfaces = g_a2j_dbus_interfaces;
|
|
|
|
if (!dbus_connection_register_object_path(
|
|
g_dbus_connection_ptr,
|
|
A2J_DBUS_OBJECT_PATH,
|
|
&vtable,
|
|
&g_a2j_dbus_object_descriptor))
|
|
{
|
|
a2j_error("Ran out of memory trying to register D-Bus object path");
|
|
goto fail_unref_dbus_connection;
|
|
}
|
|
|
|
return true;
|
|
|
|
fail_unref_dbus_connection:
|
|
dbus_connection_unref(g_dbus_connection_ptr);
|
|
g_dbus_connection_ptr = NULL;
|
|
|
|
fail:
|
|
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
a2j_dbus_run(
|
|
int timeout_milliseconds)
|
|
{
|
|
return dbus_connection_read_write_dispatch(g_dbus_connection_ptr, timeout_milliseconds);
|
|
}
|
|
|
|
void
|
|
a2j_dbus_uninit(void)
|
|
{
|
|
dbus_connection_unref(g_dbus_connection_ptr);
|
|
g_dbus_connection_ptr = NULL;
|
|
}
|
|
|
|
void
|
|
a2j_dbus_signal(
|
|
const char * path,
|
|
const char * interface,
|
|
const char * name,
|
|
int type,
|
|
...)
|
|
{
|
|
va_list ap;
|
|
DBusMessage * message_ptr;
|
|
|
|
a2j_debug("Sending signal %s.%s from %s", interface, name, path);
|
|
|
|
if (!a2j_dbus_is_available())
|
|
{
|
|
a2j_error("Internal error: cannot send D-Bus signal without D-Bus being initialized");
|
|
return;
|
|
}
|
|
|
|
message_ptr = dbus_message_new_signal (path, interface, name);
|
|
|
|
if (message_ptr == NULL)
|
|
{
|
|
a2j_error("Ran out of memory trying to create new signal");
|
|
return;
|
|
}
|
|
|
|
va_start(ap, type);
|
|
if (dbus_message_append_args_valist(message_ptr, type, ap))
|
|
{
|
|
if (!dbus_connection_send(g_dbus_connection_ptr, message_ptr, NULL))
|
|
{
|
|
a2j_error("Ran out of memory trying to queue signal");
|
|
}
|
|
|
|
dbus_connection_flush(g_dbus_connection_ptr);
|
|
}
|
|
else
|
|
{
|
|
a2j_error("Ran out of memory trying to append signal argument(s)");
|
|
}
|
|
va_end(ap);
|
|
|
|
dbus_message_unref(message_ptr);
|
|
}
|