2008-05-26 12:56:22 +03:00
|
|
|
/* -*- Mode: C ; c-basic-offset: 4 -*- */
|
|
|
|
/*
|
2010-08-28 03:11:59 +03:00
|
|
|
Copyright (C) 2007,2008,2010 Nedko Arnaudov
|
2008-05-26 12:56:22 +03:00
|
|
|
Copyright (C) 2007-2008 Juuso Alasuutari
|
|
|
|
Copyright (C) 2008 Marc-Olivier Barre
|
|
|
|
|
|
|
|
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; either 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2008-06-02 23:46:33 +03:00
|
|
|
#if defined(HAVE_CONFIG_H)
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
2008-05-26 12:56:22 +03:00
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <dbus/dbus.h>
|
|
|
|
#include <pthread.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include "config.h"
|
2023-04-18 21:43:20 +03:00
|
|
|
#include "version.h"
|
2008-05-26 12:56:22 +03:00
|
|
|
|
|
|
|
#include "jackdbus.h"
|
|
|
|
#include "controller.h"
|
|
|
|
#include "jack/jack.h"
|
|
|
|
#include "jack/jslist.h"
|
|
|
|
#include "jack/control.h"
|
2023-11-07 02:44:43 +02:00
|
|
|
#if SIGINFO_ENABLED
|
|
|
|
#include "../siginfo/siginfo.h"
|
|
|
|
#endif
|
2008-05-26 12:56:22 +03:00
|
|
|
|
2010-08-28 03:11:59 +03:00
|
|
|
static char * g_log_filename;
|
|
|
|
static ino_t g_log_file_ino;
|
2008-05-26 12:56:22 +03:00
|
|
|
FILE *g_logfile;
|
|
|
|
char *g_jackdbus_config_dir;
|
|
|
|
size_t g_jackdbus_config_dir_len; /* without terminating '\0' char */
|
|
|
|
char *g_jackdbus_log_dir;
|
|
|
|
size_t g_jackdbus_log_dir_len; /* without terminating '\0' char */
|
|
|
|
int g_exit_command;
|
|
|
|
DBusConnection *g_connection;
|
|
|
|
|
|
|
|
void
|
|
|
|
jack_dbus_send_signal(
|
|
|
|
const char *sender_object_path,
|
|
|
|
const char *iface,
|
|
|
|
const char *signal_name,
|
|
|
|
int first_arg_type,
|
|
|
|
...)
|
|
|
|
{
|
|
|
|
DBusMessage *message_ptr;
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
va_start(ap, first_arg_type);
|
|
|
|
|
|
|
|
message_ptr = dbus_message_new_signal(sender_object_path, iface, signal_name);
|
|
|
|
if (message_ptr == NULL)
|
|
|
|
{
|
|
|
|
jack_error("dbus_message_new_signal() failed.");
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!dbus_message_append_args_valist(message_ptr, first_arg_type, ap))
|
|
|
|
{
|
|
|
|
jack_error("dbus_message_append_args_valist() failed.");
|
|
|
|
goto unref;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add message to outgoing message queue */
|
|
|
|
if (!dbus_connection_send(g_connection, message_ptr, NULL))
|
|
|
|
{
|
|
|
|
jack_error("dbus_connection_send() failed.");
|
|
|
|
goto unref;
|
|
|
|
}
|
|
|
|
|
|
|
|
unref:
|
|
|
|
dbus_message_unref(message_ptr);
|
|
|
|
|
|
|
|
exit:
|
|
|
|
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
|
|
|
|
jack_dbus_send_method_return(
|
|
|
|
struct jack_dbus_method_call * call)
|
|
|
|
{
|
2011-08-11 08:33:34 +03:00
|
|
|
if (call->message == NULL)
|
|
|
|
{
|
|
|
|
/* async call */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-05-26 12:56:22 +03:00
|
|
|
if (call->reply)
|
|
|
|
{
|
|
|
|
retry_send:
|
|
|
|
if (!dbus_connection_send (call->connection, call->reply, NULL))
|
|
|
|
{
|
|
|
|
jack_error ("Ran out of memory trying to queue method return");
|
|
|
|
}
|
|
|
|
|
|
|
|
dbus_connection_flush (call->connection);
|
|
|
|
dbus_message_unref (call->reply);
|
|
|
|
call->reply = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
jack_error ("send_method_return() called with a NULL message,"
|
|
|
|
" trying to construct a void return...");
|
|
|
|
|
|
|
|
if ((call->reply = dbus_message_new_method_return (call->message)))
|
|
|
|
{
|
|
|
|
goto retry_send;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
jack_error ("Failed to construct method return!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define object_ptr ((struct jack_dbus_object_descriptor *)data)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The D-Bus message handler for object path /org/jackaudio/Controller.
|
|
|
|
*/
|
|
|
|
DBusHandlerResult
|
|
|
|
jack_dbus_message_handler(
|
|
|
|
DBusConnection *connection,
|
|
|
|
DBusMessage *message,
|
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
struct jack_dbus_method_call call;
|
|
|
|
const char *interface_name;
|
|
|
|
struct jack_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)))
|
|
|
|
{
|
|
|
|
jack_dbus_error(
|
|
|
|
&call,
|
|
|
|
JACK_DBUS_ERROR_UNKNOWN_METHOD,
|
|
|
|
"Received method call with empty method name");
|
|
|
|
goto send_return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialize our data. */
|
|
|
|
call.context = object_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.
|
2019-08-23 01:30:45 +03:00
|
|
|
* The interface handler functions only return false if the
|
2008-05-26 12:56:22 +03:00
|
|
|
* method name was unknown, otherwise they run the specified
|
|
|
|
* method and return TRUE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
interface_ptr_ptr = object_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 = object_ptr->interfaces;
|
|
|
|
|
|
|
|
while (*interface_ptr_ptr != NULL)
|
|
|
|
{
|
|
|
|
if ((*interface_ptr_ptr)->handler(&call, (*interface_ptr_ptr)->methods))
|
|
|
|
{
|
|
|
|
goto send_return;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface_ptr_ptr++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
jack_dbus_error(
|
|
|
|
&call,
|
|
|
|
JACK_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:
|
|
|
|
jack_dbus_send_method_return(&call);
|
|
|
|
|
|
|
|
handled:
|
|
|
|
return DBUS_HANDLER_RESULT_HANDLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
jack_dbus_message_handler_unregister(
|
|
|
|
DBusConnection *connection,
|
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
jack_info ("Message handler was unregistered");
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef object_ptr
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if the supplied method name exists in org.jackaudio.JackConfigure,
|
|
|
|
* if it does execute it and return TRUE. Otherwise return FALSE.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
jack_dbus_run_method(
|
|
|
|
struct jack_dbus_method_call *call,
|
|
|
|
const struct jack_dbus_interface_method_descriptor * methods)
|
|
|
|
{
|
|
|
|
const struct jack_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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read arguments from a method call.
|
|
|
|
* If the operation fails construct an error and return false,
|
|
|
|
* otherwise return true.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
jack_dbus_get_method_args(
|
|
|
|
struct jack_dbus_method_call *call,
|
|
|
|
int type,
|
|
|
|
...)
|
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
DBusError error;
|
|
|
|
bool retval = true;
|
|
|
|
|
|
|
|
va_start (args, type);
|
|
|
|
dbus_error_init (&error);
|
|
|
|
|
|
|
|
if (!dbus_message_get_args_valist (call->message, &error, type, args))
|
|
|
|
{
|
|
|
|
jack_dbus_error (call, JACK_DBUS_ERROR_INVALID_ARGS,
|
|
|
|
"Invalid arguments to method \"%s\"",
|
|
|
|
call->method_name);
|
|
|
|
retval = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
dbus_error_free (&error);
|
|
|
|
va_end (args);
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read a string and a variant argument from a method call.
|
|
|
|
* If the operation fails construct an error and return false,
|
|
|
|
* otherwise return true.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
jack_dbus_get_method_args_string_and_variant(
|
|
|
|
struct jack_dbus_method_call *call,
|
|
|
|
const char **arg1,
|
|
|
|
message_arg_t *arg2,
|
|
|
|
int *type_ptr)
|
|
|
|
{
|
|
|
|
DBusMessageIter iter, sub_iter;
|
|
|
|
|
|
|
|
/* First we want a string... */
|
|
|
|
if (dbus_message_iter_init (call->message, &iter)
|
|
|
|
&& dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_STRING)
|
|
|
|
{
|
|
|
|
dbus_message_iter_get_basic (&iter, arg1);
|
|
|
|
dbus_message_iter_next (&iter);
|
|
|
|
|
|
|
|
/* ...and then a variant. */
|
|
|
|
if (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_VARIANT)
|
|
|
|
{
|
|
|
|
dbus_message_iter_recurse (&iter, &sub_iter);
|
|
|
|
dbus_message_iter_get_basic (&sub_iter, arg2);
|
|
|
|
*type_ptr = dbus_message_iter_get_arg_type (&sub_iter);
|
|
|
|
|
|
|
|
/* Got what we wanted. */
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
jack_dbus_error (call, JACK_DBUS_ERROR_INVALID_ARGS,
|
|
|
|
"Invalid arguments to method \"%s\"",
|
|
|
|
call->method_name);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-09-03 13:57:27 +03:00
|
|
|
/*
|
|
|
|
* Read two strings and a variant argument from a method call.
|
|
|
|
* If the operation fails construct an error and return false,
|
|
|
|
* otherwise return true.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
jack_dbus_get_method_args_two_strings_and_variant(
|
|
|
|
struct jack_dbus_method_call *call,
|
|
|
|
const char **arg1,
|
|
|
|
const char **arg2,
|
|
|
|
message_arg_t *arg3,
|
|
|
|
int *type_ptr)
|
|
|
|
{
|
|
|
|
DBusMessageIter iter, sub_iter;
|
|
|
|
|
|
|
|
/* First we want a string... */
|
|
|
|
if (dbus_message_iter_init (call->message, &iter)
|
|
|
|
&& dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_STRING)
|
|
|
|
{
|
|
|
|
dbus_message_iter_get_basic (&iter, arg1);
|
|
|
|
dbus_message_iter_next (&iter);
|
|
|
|
|
|
|
|
/* ...and then a second string. */
|
|
|
|
if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Got what we wanted. */
|
|
|
|
dbus_message_iter_get_basic (&iter, arg2);
|
|
|
|
dbus_message_iter_next (&iter);
|
|
|
|
|
|
|
|
/* ...and then a variant. */
|
|
|
|
if (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_VARIANT)
|
|
|
|
{
|
|
|
|
dbus_message_iter_recurse (&iter, &sub_iter);
|
|
|
|
dbus_message_iter_get_basic (&sub_iter, arg3);
|
|
|
|
*type_ptr = dbus_message_iter_get_arg_type (&sub_iter);
|
|
|
|
|
|
|
|
/* Got what we wanted. */
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
jack_dbus_error (call, JACK_DBUS_ERROR_INVALID_ARGS,
|
|
|
|
"Invalid arguments to method \"%s\"",
|
|
|
|
call->method_name);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-05-26 12:56:22 +03:00
|
|
|
/*
|
|
|
|
* Append a variant type to a D-Bus message.
|
|
|
|
* Return false if something fails, true otherwise.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
jack_dbus_message_append_variant(
|
|
|
|
DBusMessageIter *iter,
|
|
|
|
int type,
|
|
|
|
const char *signature,
|
|
|
|
message_arg_t *arg)
|
|
|
|
{
|
|
|
|
DBusMessageIter sub_iter;
|
|
|
|
|
|
|
|
/* Open a variant container. */
|
|
|
|
if (!dbus_message_iter_open_container (iter, DBUS_TYPE_VARIANT, signature, &sub_iter))
|
|
|
|
{
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Append the supplied value. */
|
|
|
|
if (!dbus_message_iter_append_basic (&sub_iter, type, (const void *) arg))
|
|
|
|
{
|
|
|
|
dbus_message_iter_close_container (iter, &sub_iter);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Close the container. */
|
|
|
|
if (!dbus_message_iter_close_container (iter, &sub_iter))
|
|
|
|
{
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Construct an empty method return 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
|
|
|
|
jack_dbus_construct_method_return_empty(
|
|
|
|
struct jack_dbus_method_call * call)
|
|
|
|
{
|
|
|
|
call->reply = dbus_message_new_method_return (call->message);
|
|
|
|
|
|
|
|
if (call->reply == NULL)
|
|
|
|
{
|
|
|
|
jack_error ("Ran out of memory trying to construct method return");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
jack_dbus_construct_method_return_single(
|
|
|
|
struct jack_dbus_method_call *call,
|
|
|
|
int type,
|
|
|
|
message_arg_t arg)
|
|
|
|
{
|
|
|
|
DBusMessageIter iter;
|
|
|
|
call->reply = dbus_message_new_method_return (call->message);
|
|
|
|
|
|
|
|
if (call->reply == NULL)
|
|
|
|
{
|
|
|
|
goto fail_no_mem;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Void method return requested by caller. */
|
|
|
|
if (type == DBUS_TYPE_INVALID)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Prevent crash on NULL input string. */
|
|
|
|
else if (type == DBUS_TYPE_STRING && arg.string == NULL)
|
|
|
|
{
|
|
|
|
arg.string = "";
|
|
|
|
}
|
|
|
|
|
|
|
|
dbus_message_iter_init_append (call->reply, &iter);
|
|
|
|
|
|
|
|
if (!dbus_message_iter_append_basic (&iter, type, (const void *) &arg))
|
|
|
|
{
|
|
|
|
dbus_message_unref (call->reply);
|
|
|
|
call->reply = NULL;
|
|
|
|
goto fail_no_mem;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
fail_no_mem:
|
|
|
|
jack_error ("Ran out of memory trying to construct method return");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Construct a method return which holds an array of strings.
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
jack_dbus_construct_method_return_array_of_strings(
|
|
|
|
struct jack_dbus_method_call *call,
|
|
|
|
unsigned int num_members,
|
|
|
|
const char **array)
|
|
|
|
{
|
|
|
|
DBusMessageIter iter, sub_iter;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
call->reply = dbus_message_new_method_return (call->message);
|
|
|
|
if (!call->reply)
|
|
|
|
{
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
dbus_message_iter_init_append (call->reply, &iter);
|
|
|
|
|
|
|
|
if (!dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "s", &sub_iter))
|
|
|
|
{
|
|
|
|
goto fail_unref;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < num_members; ++i)
|
|
|
|
{
|
|
|
|
if (!dbus_message_iter_append_basic (&sub_iter, DBUS_TYPE_STRING, (const void *) &array[i]))
|
|
|
|
{
|
|
|
|
dbus_message_iter_close_container (&iter, &sub_iter);
|
|
|
|
goto fail_unref;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!dbus_message_iter_close_container (&iter, &sub_iter))
|
|
|
|
{
|
|
|
|
goto fail_unref;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
fail_unref:
|
|
|
|
dbus_message_unref (call->reply);
|
|
|
|
call->reply = NULL;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
jack_error ("Ran out of memory trying to construct method return");
|
|
|
|
}
|
|
|
|
|
2010-08-28 03:11:59 +03:00
|
|
|
static bool jack_dbus_log_open(void)
|
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
int ret;
|
|
|
|
int retry;
|
|
|
|
|
|
|
|
if (g_logfile != NULL)
|
|
|
|
{
|
|
|
|
ret = stat(g_log_filename, &st);
|
|
|
|
if (ret != 0 || g_log_file_ino != st.st_ino)
|
|
|
|
{
|
|
|
|
fclose(g_logfile);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (retry = 0; retry < 10; retry++)
|
|
|
|
{
|
|
|
|
g_logfile = fopen(g_log_filename, "a");
|
|
|
|
if (g_logfile == NULL)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Cannot open jackdbus log file \"%s\": %d (%s)\n", g_log_filename, errno, strerror(errno));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = stat(g_log_filename, &st);
|
|
|
|
if (ret == 0)
|
|
|
|
{
|
|
|
|
g_log_file_ino = st.st_ino;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(g_logfile);
|
|
|
|
g_logfile = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stderr, "Cannot stat just opened jackdbus log file \"%s\": %d (%s). %d retries\n", g_log_filename, errno, strerror(errno), retry);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-05-26 12:56:22 +03:00
|
|
|
void
|
|
|
|
jack_dbus_info_callback(const char *msg)
|
|
|
|
{
|
|
|
|
time_t timestamp;
|
|
|
|
char timestamp_str[26];
|
|
|
|
|
|
|
|
time(×tamp);
|
|
|
|
ctime_r(×tamp, timestamp_str);
|
|
|
|
timestamp_str[24] = 0;
|
|
|
|
|
2010-08-28 03:11:59 +03:00
|
|
|
if (jack_dbus_log_open())
|
|
|
|
{
|
|
|
|
fprintf(g_logfile, "%s: %s\n", timestamp_str, msg);
|
|
|
|
fflush(g_logfile);
|
|
|
|
}
|
2008-05-26 12:56:22 +03:00
|
|
|
}
|
|
|
|
|
2008-05-29 00:14:51 +03:00
|
|
|
#define ANSI_BOLD_ON "\033[1m"
|
|
|
|
#define ANSI_BOLD_OFF "\033[22m"
|
|
|
|
#define ANSI_COLOR_RED "\033[31m"
|
|
|
|
#define ANSI_RESET "\033[0m"
|
|
|
|
|
2008-05-26 12:56:22 +03:00
|
|
|
void
|
|
|
|
jack_dbus_error_callback(const char *msg)
|
|
|
|
{
|
|
|
|
time_t timestamp;
|
|
|
|
char timestamp_str[26];
|
|
|
|
|
|
|
|
time(×tamp);
|
|
|
|
ctime_r(×tamp, timestamp_str);
|
|
|
|
timestamp_str[24] = 0;
|
|
|
|
|
2010-08-28 03:11:59 +03:00
|
|
|
if (jack_dbus_log_open())
|
|
|
|
{
|
|
|
|
fprintf(g_logfile, "%s: " ANSI_BOLD_ON ANSI_COLOR_RED "ERROR: %s" ANSI_RESET "\n", timestamp_str, msg);
|
|
|
|
fflush(g_logfile);
|
|
|
|
}
|
2008-05-26 12:56:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
ensure_dir_exist(const char *dirname, int mode)
|
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
if (stat(dirname, &st) != 0)
|
|
|
|
{
|
|
|
|
if (errno == ENOENT)
|
|
|
|
{
|
|
|
|
printf("Directory \"%s\" does not exist. Creating...\n", dirname);
|
|
|
|
if (mkdir(dirname, mode) != 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Failed to create \"%s\" directory: %d (%s)\n", dirname, errno, strerror(errno));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Failed to stat \"%s\": %d (%s)\n", dirname, errno, strerror(errno));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!S_ISDIR(st.st_mode))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "\"%s\" exists but is not directory.\n", dirname);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
|
|
|
pathname_cat(const char *pathname_a, const char *pathname_b)
|
|
|
|
{
|
|
|
|
char *pathname;
|
|
|
|
int pathname_a_len, pathname_b_len, pathname_len;
|
|
|
|
pathname_a_len = strlen(pathname_a);
|
|
|
|
pathname_b_len = strlen(pathname_b);
|
|
|
|
pathname = malloc(pathname_a_len + pathname_b_len + 1);
|
|
|
|
if (pathname == NULL)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Out of memory\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
memcpy(pathname, pathname_a, pathname_a_len);
|
|
|
|
memcpy(pathname + pathname_a_len, pathname_b, pathname_b_len);
|
|
|
|
pathname_len = pathname_a_len + pathname_b_len;
|
|
|
|
pathname[pathname_len] = 0;
|
|
|
|
return pathname;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2023-11-07 21:06:01 +02:00
|
|
|
paths_init(void)
|
2008-05-26 12:56:22 +03:00
|
|
|
{
|
|
|
|
const char *home_dir, *xdg_config_home, *xdg_log_home;
|
|
|
|
|
|
|
|
home_dir = getenv("HOME");
|
|
|
|
if (home_dir == NULL)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Environment variable HOME not set\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
xdg_config_home = getenv("XDG_CONFIG_HOME");
|
|
|
|
if (xdg_config_home == NULL)
|
|
|
|
{
|
|
|
|
if (!(xdg_config_home = pathname_cat(home_dir, DEFAULT_XDG_CONFIG))) goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(xdg_log_home = pathname_cat(home_dir, DEFAULT_XDG_LOG))) goto fail;
|
|
|
|
|
|
|
|
if (!(g_jackdbus_config_dir = pathname_cat(xdg_config_home, JACKDBUS_DIR))) goto fail;
|
|
|
|
if (!(g_jackdbus_log_dir = pathname_cat(xdg_log_home, JACKDBUS_DIR))) goto fail;
|
|
|
|
|
|
|
|
if (!ensure_dir_exist(xdg_config_home, 0700))
|
|
|
|
{
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ensure_dir_exist(xdg_log_home, 0700))
|
|
|
|
{
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ensure_dir_exist(g_jackdbus_config_dir, 0700))
|
|
|
|
{
|
|
|
|
free(g_jackdbus_config_dir);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
g_jackdbus_config_dir_len = strlen(g_jackdbus_config_dir);
|
|
|
|
|
|
|
|
if (!ensure_dir_exist(g_jackdbus_log_dir, 0700))
|
|
|
|
{
|
|
|
|
free(g_jackdbus_log_dir);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
g_jackdbus_log_dir_len = strlen(g_jackdbus_log_dir);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2023-11-07 21:06:01 +02:00
|
|
|
paths_uninit(void)
|
2008-05-26 12:56:22 +03:00
|
|
|
{
|
|
|
|
free(g_jackdbus_config_dir);
|
|
|
|
free(g_jackdbus_log_dir);
|
|
|
|
}
|
|
|
|
|
2010-08-28 03:11:59 +03:00
|
|
|
static bool log_init(void)
|
2008-05-26 12:56:22 +03:00
|
|
|
{
|
|
|
|
size_t log_len;
|
|
|
|
|
|
|
|
log_len = strlen(JACKDBUS_LOG);
|
|
|
|
|
2010-08-28 03:11:59 +03:00
|
|
|
g_log_filename = malloc(g_jackdbus_log_dir_len + log_len + 1);
|
|
|
|
if (g_log_filename == NULL)
|
2008-05-26 12:56:22 +03:00
|
|
|
{
|
|
|
|
fprintf(stderr, "Out of memory\n");
|
2010-08-28 03:11:59 +03:00
|
|
|
return false;
|
2008-05-26 12:56:22 +03:00
|
|
|
}
|
|
|
|
|
2010-08-28 03:11:59 +03:00
|
|
|
memcpy(g_log_filename, g_jackdbus_log_dir, g_jackdbus_log_dir_len);
|
|
|
|
memcpy(g_log_filename + g_jackdbus_log_dir_len, JACKDBUS_LOG, log_len);
|
|
|
|
g_log_filename[g_jackdbus_log_dir_len + log_len] = 0;
|
2008-05-26 12:56:22 +03:00
|
|
|
|
2010-08-28 03:11:59 +03:00
|
|
|
if (!jack_dbus_log_open())
|
2008-05-26 12:56:22 +03:00
|
|
|
{
|
2010-08-28 03:11:59 +03:00
|
|
|
return false;
|
2008-05-26 12:56:22 +03:00
|
|
|
}
|
|
|
|
|
2010-08-28 03:11:59 +03:00
|
|
|
return true;
|
2008-05-26 12:56:22 +03:00
|
|
|
}
|
|
|
|
|
2010-08-28 03:11:59 +03:00
|
|
|
static void log_uninit(void)
|
2008-05-26 12:56:22 +03:00
|
|
|
{
|
2010-08-28 03:11:59 +03:00
|
|
|
if (g_logfile != NULL)
|
|
|
|
{
|
|
|
|
fclose(g_logfile);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(g_log_filename);
|
2008-05-26 12:56:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
jack_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);
|
|
|
|
|
|
|
|
jack_error_callback(buffer);
|
|
|
|
if (dbus_call_context_ptr != NULL)
|
|
|
|
{
|
2008-10-19 23:46:35 +03:00
|
|
|
if (((struct jack_dbus_method_call *)dbus_call_context_ptr)->reply != NULL)
|
|
|
|
{
|
|
|
|
dbus_message_unref(((struct jack_dbus_method_call *)dbus_call_context_ptr)->reply);
|
|
|
|
((struct jack_dbus_method_call *)dbus_call_context_ptr)->reply = NULL;
|
|
|
|
}
|
|
|
|
|
2008-05-26 12:56:22 +03:00
|
|
|
((struct jack_dbus_method_call *)dbus_call_context_ptr)->reply = dbus_message_new_error(
|
|
|
|
((struct jack_dbus_method_call *)dbus_call_context_ptr)->message,
|
|
|
|
error_name,
|
|
|
|
buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
2009-12-06 21:00:17 +02:00
|
|
|
void
|
|
|
|
jack_dbus_only_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);
|
|
|
|
|
|
|
|
if (((struct jack_dbus_method_call *)dbus_call_context_ptr)->reply != NULL)
|
|
|
|
{
|
|
|
|
dbus_message_unref(((struct jack_dbus_method_call *)dbus_call_context_ptr)->reply);
|
|
|
|
((struct jack_dbus_method_call *)dbus_call_context_ptr)->reply = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
((struct jack_dbus_method_call *)dbus_call_context_ptr)->reply = dbus_message_new_error(
|
|
|
|
((struct jack_dbus_method_call *)dbus_call_context_ptr)->message,
|
|
|
|
error_name,
|
|
|
|
buffer);
|
|
|
|
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
2008-05-26 12:56:22 +03:00
|
|
|
int
|
|
|
|
main (int argc, char **argv)
|
|
|
|
{
|
|
|
|
DBusError error;
|
|
|
|
int ret;
|
2008-05-29 02:37:14 +03:00
|
|
|
void *controller_ptr;
|
|
|
|
struct stat st;
|
|
|
|
char timestamp_str[26];
|
|
|
|
|
|
|
|
st.st_mtime = 0;
|
|
|
|
stat(argv[0], &st);
|
|
|
|
ctime_r(&st.st_mtime, timestamp_str);
|
|
|
|
timestamp_str[24] = 0;
|
2008-05-26 12:56:22 +03:00
|
|
|
|
|
|
|
if (!jack_controller_settings_init())
|
|
|
|
{
|
|
|
|
ret = 1;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (argc != 2 || strcmp(argv[1], "auto") != 0)
|
|
|
|
{
|
|
|
|
ret = 0;
|
|
|
|
fprintf(
|
|
|
|
stderr,
|
|
|
|
"jackdbus should be auto-executed by D-Bus message bus daemon.\n"
|
|
|
|
"If you want to run it manually anyway, specify \"auto\" as only parameter\n");
|
|
|
|
goto fail_uninit_xml;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!paths_init())
|
|
|
|
{
|
|
|
|
ret = 1;
|
|
|
|
goto fail_uninit_xml;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!log_init())
|
|
|
|
{
|
|
|
|
ret = 1;
|
|
|
|
goto fail_uninit_paths;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if !defined(DISABLE_SIGNAL_MAGIC)
|
|
|
|
jackctl_setup_signals(0);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
jack_set_error_function(jack_dbus_error_callback);
|
|
|
|
jack_set_info_function(jack_dbus_info_callback);
|
|
|
|
|
2023-11-07 02:44:43 +02:00
|
|
|
#if SIGINFO_ENABLED
|
|
|
|
/* setup our SIGSEGV magic that prints nice stack in our logfile */
|
|
|
|
setup_siginfo();
|
|
|
|
#endif
|
2008-05-29 01:22:59 +03:00
|
|
|
|
2008-05-26 12:56:22 +03:00
|
|
|
jack_info("------------------");
|
2023-04-18 21:43:20 +03:00
|
|
|
jack_info("jackdbus version %s built from %s on %s", JACK_VERSION, GIT_VERSION, timestamp_str);
|
|
|
|
jack_info("JACK server version %s", jack_get_version_string());
|
|
|
|
jack_info("jackdbus controller activated.");
|
2008-05-26 12:56:22 +03:00
|
|
|
|
|
|
|
if (!dbus_threads_init_default())
|
|
|
|
{
|
|
|
|
jack_error("dbus_threads_init_default() failed");
|
|
|
|
ret = 1;
|
|
|
|
goto fail_uninit_log;
|
|
|
|
}
|
|
|
|
|
|
|
|
dbus_error_init (&error);
|
|
|
|
g_connection = dbus_bus_get (DBUS_BUS_SESSION, &error);
|
|
|
|
if (dbus_error_is_set (&error))
|
|
|
|
{
|
|
|
|
jack_error("Cannot connect to D-Bus session bus: %s", error.message);
|
|
|
|
ret = 1;
|
|
|
|
goto fail_uninit_log;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = dbus_bus_request_name(
|
|
|
|
g_connection,
|
|
|
|
"org.jackaudio.service",
|
|
|
|
DBUS_NAME_FLAG_DO_NOT_QUEUE,
|
|
|
|
&error);
|
|
|
|
if (ret == -1)
|
|
|
|
{
|
|
|
|
jack_error("Cannot request service name: %s", error.message);
|
|
|
|
dbus_error_free(&error);
|
|
|
|
ret = 1;
|
|
|
|
goto fail_unref_connection;
|
|
|
|
}
|
|
|
|
else if (ret == DBUS_REQUEST_NAME_REPLY_EXISTS)
|
|
|
|
{
|
|
|
|
jack_error("Requested D-Bus service name already exists");
|
|
|
|
ret = 1;
|
|
|
|
goto fail_unref_connection;
|
|
|
|
}
|
|
|
|
|
|
|
|
controller_ptr = jack_controller_create(g_connection);
|
|
|
|
|
|
|
|
if (controller_ptr == NULL)
|
|
|
|
{
|
|
|
|
ret = 1;
|
|
|
|
goto fail_unref_connection;
|
|
|
|
}
|
|
|
|
|
|
|
|
jack_info("Listening for D-Bus messages");
|
|
|
|
|
|
|
|
g_exit_command = FALSE;
|
2011-11-12 21:23:44 +02:00
|
|
|
while (!g_exit_command && dbus_connection_read_write_dispatch (g_connection, 200))
|
|
|
|
{
|
|
|
|
jack_controller_run(controller_ptr);
|
|
|
|
}
|
2008-05-26 12:56:22 +03:00
|
|
|
|
|
|
|
jack_controller_destroy(controller_ptr);
|
|
|
|
|
|
|
|
jack_info("Controller deactivated.");
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
fail_unref_connection:
|
|
|
|
dbus_connection_unref(g_connection);
|
|
|
|
|
|
|
|
fail_uninit_log:
|
|
|
|
log_uninit();
|
|
|
|
|
|
|
|
fail_uninit_paths:
|
|
|
|
paths_uninit();
|
|
|
|
|
|
|
|
fail_uninit_xml:
|
|
|
|
jack_controller_settings_uninit();
|
|
|
|
|
|
|
|
fail:
|
|
|
|
return ret;
|
|
|
|
}
|