1 /* -*- Mode: C ; c-basic-offset: 4 -*- */
3 Copyright (C) 2011 Nedko Arnaudov
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #if defined(HAVE_CONFIG_H)
28 #include <dbus/dbus.h>
31 #include "controller_internal.h"
32 #include "jack/session.h"
33 #include "jack/control.h"
35 #define JACK_DBUS_IFACE_NAME "org.jackaudio.SessionManager"
39 jack_controller_control_send_signal_session_state_changed(
40 jack_session_event_type_t type
,
51 jack_dbus_send_signal(
52 JACK_CONTROLLER_OBJECT_PATH
,
62 static bool start_detached_thread(void * (* start_routine
)(void *), void * arg
)
65 static pthread_attr_t attr
;
68 ret
= pthread_attr_init(&attr
);
71 jack_error("pthread_attr_init() failed with %d", ret
);
75 ret
= pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
78 jack_error("pthread_attr_setdetachstate() failed with %d", ret
);
82 ret
= pthread_create(&tid
, &attr
, start_routine
, arg
);
85 jack_error("pthread_create() failed with %d", ret
);
89 jack_log("Detached thread %d created", (int)tid
);
92 pthread_attr_destroy(&attr
);
97 static void send_session_notify_reply(struct jack_session_pending_command
* pending_cmd_ptr
, jack_session_command_t
* commands
)
99 struct jack_dbus_method_call call
;
100 const jack_session_command_t
* cmd_ptr
;
101 DBusMessageIter top_iter
, array_iter
, struct_iter
;
104 /* jack_dbus_error() wants call struct */
105 call
.message
= pending_cmd_ptr
->message
;
106 call
.connection
= pending_cmd_ptr
->connection
;
108 if (commands
== NULL
)
110 jack_dbus_error(&call
, JACK_DBUS_ERROR_GENERIC
, "jack_session_notify() failed");
114 jack_info("Session notify complete, commands follow:");
116 call
.reply
= dbus_message_new_method_return(pending_cmd_ptr
->message
);
117 if (call
.reply
== NULL
)
122 dbus_message_iter_init_append(call
.reply
, &top_iter
);
124 if (!dbus_message_iter_open_container(&top_iter
, DBUS_TYPE_ARRAY
, "(sssu)", &array_iter
))
129 for (cmd_ptr
= commands
; cmd_ptr
->uuid
!= NULL
; cmd_ptr
++)
131 if (!dbus_message_iter_open_container(&array_iter
, DBUS_TYPE_STRUCT
, NULL
, &struct_iter
))
136 if (!dbus_message_iter_append_basic(&struct_iter
, DBUS_TYPE_STRING
, &cmd_ptr
->uuid
))
141 if (!dbus_message_iter_append_basic(&struct_iter
, DBUS_TYPE_STRING
, &cmd_ptr
->client_name
))
146 if (!dbus_message_iter_append_basic(&struct_iter
, DBUS_TYPE_STRING
, &cmd_ptr
->command
))
151 u32
= cmd_ptr
->flags
;
152 if (!dbus_message_iter_append_basic(&struct_iter
, DBUS_TYPE_UINT32
, &u32
))
157 jack_info("uuid='%s', client='%s', command='%s', flags=0x%"PRIX32
, cmd_ptr
->uuid
, cmd_ptr
->client_name
, cmd_ptr
->command
, u32
);
159 if (!dbus_message_iter_close_container(&array_iter
, &struct_iter
))
165 jack_info("End of session commands.");
167 if (!dbus_message_iter_close_container(&top_iter
, &array_iter
))
175 dbus_message_iter_close_container(&array_iter
, &struct_iter
);
177 dbus_message_iter_close_container(&top_iter
, &array_iter
);
179 dbus_message_unref(call
.reply
);
183 if (call
.reply
!= NULL
)
185 if (!dbus_connection_send(pending_cmd_ptr
->connection
, call
.reply
, NULL
))
187 jack_error("Ran out of memory trying to queue method return");
190 dbus_connection_flush(pending_cmd_ptr
->connection
);
191 dbus_message_unref(call
.reply
);
196 jack_error("Ran out of memory trying to construct method return");
200 #define controller_ptr ((struct jack_controller *)context)
201 void * jack_controller_process_session_command_thread(void * context
)
203 struct jack_session_pending_command
* pending_cmd_ptr
;
204 jack_session_command_t
* commands
;
206 jack_log("jack_controller_process_session_command_thread enter");
208 pthread_mutex_lock(&controller_ptr
->lock
);
210 /* get next command */
211 assert(!list_empty(&controller_ptr
->session_pending_commands
));
212 pending_cmd_ptr
= list_entry(controller_ptr
->session_pending_commands
.next
, struct jack_session_pending_command
, siblings
);
213 pthread_mutex_unlock(&controller_ptr
->lock
);
215 jack_info("Session notify initiated. target='%s', type=%d, path='%s'", pending_cmd_ptr
->target
, (int)pending_cmd_ptr
->type
, pending_cmd_ptr
->path
);
217 jack_controller_control_send_signal_session_state_changed(pending_cmd_ptr
->type
, pending_cmd_ptr
->target
);
219 commands
= jack_session_notify(controller_ptr
->client
, pending_cmd_ptr
->target
, pending_cmd_ptr
->type
, pending_cmd_ptr
->path
);
220 send_session_notify_reply(pending_cmd_ptr
, commands
);
221 if (commands
!= NULL
)
223 jack_session_commands_free(commands
);
226 pthread_mutex_lock(&controller_ptr
->lock
);
228 /* keep state consistent by sending signal after to lock */
229 /* otherwise the main thread may receive not-to-be-queued request and fail */
230 jack_controller_control_send_signal_session_state_changed(0, NULL
);
232 /* remove the head of the list (queue) */
233 assert(!list_empty(&controller_ptr
->session_pending_commands
));
234 assert(pending_cmd_ptr
== list_entry(controller_ptr
->session_pending_commands
.next
, struct jack_session_pending_command
, siblings
));
235 list_del(&pending_cmd_ptr
->siblings
);
237 /* command cleanup */
238 dbus_message_unref(pending_cmd_ptr
->message
);
239 dbus_connection_ref(pending_cmd_ptr
->connection
);
240 free(pending_cmd_ptr
);
242 /* If there are more commands, process them. Otherwise - exit the thread */
243 if (!list_empty(&controller_ptr
->session_pending_commands
))
248 pthread_mutex_unlock(&controller_ptr
->lock
);
250 jack_log("jack_controller_process_session_command_thread exit");
254 #undef controller_ptr
255 #define controller_ptr ((struct jack_controller *)call->context)
259 jack_controller_dbus_session_notify(
260 struct jack_dbus_method_call
* call
)
266 jack_session_event_type_t type
;
267 struct jack_session_pending_command
* cmd_ptr
;
269 if (!controller_ptr
->started
)
271 jack_dbus_only_error(call
, JACK_DBUS_ERROR_SERVER_NOT_RUNNING
, "Can't execute method '%s' with stopped JACK server", call
->method_name
);
275 if (!jack_dbus_get_method_args(
287 /* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */
296 type
= (jack_session_event_type_t
)u32
;
298 if (type
!= JackSessionSave
&&
299 type
!= JackSessionSaveAndQuit
&&
300 type
!= JackSessionSaveTemplate
)
302 jack_dbus_error(call
, JACK_DBUS_ERROR_INVALID_ARGS
, "Invalid session event type %" PRIu32
, u32
);
306 pthread_mutex_lock(&controller_ptr
->lock
);
307 if (list_empty(&controller_ptr
->session_pending_commands
))
309 if (!start_detached_thread(jack_controller_process_session_command_thread
, controller_ptr
))
311 jack_dbus_error(call
, JACK_DBUS_ERROR_GENERIC
, "Cannot start thread to process the command");
315 jack_log("Session notify thread started");
319 jack_dbus_error(call
, JACK_DBUS_ERROR_GENERIC
, "Busy");
323 cmd_ptr
= malloc(sizeof(struct jack_session_pending_command
));
326 jack_dbus_error(call
, JACK_DBUS_ERROR_GENERIC
, "malloc() failed for jack_session_pending_command struct");
330 cmd_ptr
->message
= dbus_message_ref(call
->message
);
331 call
->message
= NULL
; /* mark that reply will be sent asynchronously */
332 cmd_ptr
->connection
= dbus_connection_ref(call
->connection
);
334 /* it is safe to use the retrieved pointers because we already made an additional message reference */
335 cmd_ptr
->type
= type
;
336 cmd_ptr
->target
= target
;
337 cmd_ptr
->path
= path
;
339 list_add_tail(&cmd_ptr
->siblings
, &controller_ptr
->session_pending_commands
);
341 jack_log("Session notify scheduled. target='%s', type=%"PRIu32
", path='%s'", target
, u32
, path
);
344 pthread_mutex_unlock(&controller_ptr
->lock
);
349 jack_controller_dbus_get_uuid_for_client_name(
350 struct jack_dbus_method_call
* call
)
352 const char * client_name
;
355 if (!jack_dbus_get_method_args(
361 /* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */
365 client_uuid
= jack_get_uuid_for_client_name(controller_ptr
->client
, client_name
);
366 if (client_uuid
== NULL
)
368 jack_dbus_error(call
, JACK_DBUS_ERROR_GENERIC
, "jack_get_uuid_for_client_name(\"%s\") failed", client_name
);
372 jack_dbus_construct_method_return_single(call
, DBUS_TYPE_STRING
, (message_arg_t
)(const char *)client_uuid
);
378 jack_controller_dbus_get_client_name_by_uuid(
379 struct jack_dbus_method_call
* call
)
381 const char * client_uuid
;
384 if (!jack_dbus_get_method_args(
390 /* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */
394 client_name
= jack_get_client_name_by_uuid(controller_ptr
->client
, client_uuid
);
395 if (client_name
== NULL
)
397 jack_dbus_error(call
, JACK_DBUS_ERROR_GENERIC
, "jack_get_client_name_by_uuid(\"%s\") failed", client_uuid
);
401 jack_dbus_construct_method_return_single(call
, DBUS_TYPE_STRING
, (message_arg_t
)(const char *)client_name
);
407 jack_controller_dbus_reserve_client_name(
408 struct jack_dbus_method_call
* call
)
411 const char * client_name
;
412 const char * client_uuid
;
414 if (!jack_dbus_get_method_args(
422 /* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */
426 ret
= jack_reserve_client_name(controller_ptr
->client
, client_name
, client_uuid
);
429 jack_dbus_error(call
, JACK_DBUS_ERROR_GENERIC
, "jack_reserve_client_name(name=\"%s\", uuid=\"%s\") failed (%d)", client_name
, client_uuid
, ret
);
433 jack_dbus_construct_method_return_empty(call
);
438 jack_controller_dbus_has_session_callback(
439 struct jack_dbus_method_call
* call
)
442 const char * client_name
;
443 message_arg_t retval
;
445 if (!jack_dbus_get_method_args(
451 /* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */
455 ret
= jack_client_has_session_callback(controller_ptr
->client
, client_name
);
458 jack_dbus_error(call
, JACK_DBUS_ERROR_GENERIC
, "jack_client_has_session_callback(\"%s\") failed (%d)", client_name
, ret
);
462 retval
.boolean
= ret
;
463 jack_dbus_construct_method_return_single(call
, DBUS_TYPE_BOOLEAN
, retval
);
468 jack_controller_dbus_get_session_state(
469 struct jack_dbus_method_call
* call
)
471 DBusMessageIter iter
;
472 struct jack_session_pending_command
* cmd_ptr
;
477 call
->reply
= dbus_message_new_method_return(call
->message
);
478 if (call
->reply
== NULL
)
483 dbus_message_iter_init_append(call
->reply
, &iter
);
485 pthread_mutex_lock(&controller_ptr
->lock
);
487 if (list_empty(&controller_ptr
->session_pending_commands
))
494 cmd_ptr
= list_entry(controller_ptr
->session_pending_commands
.next
, struct jack_session_pending_command
, siblings
);
495 type
= (dbus_uint32_t
)cmd_ptr
->type
;
496 target
= cmd_ptr
->target
;
500 !dbus_message_iter_append_basic(&iter
, DBUS_TYPE_UINT32
, &type
) ||
501 !dbus_message_iter_append_basic(&iter
, DBUS_TYPE_STRING
, &target
);
503 pthread_mutex_unlock(&controller_ptr
->lock
);
510 dbus_message_unref(call
->reply
);
513 jack_error("Ran out of memory trying to construct method return");
516 #undef controller_ptr
518 JACK_DBUS_METHOD_ARGUMENTS_BEGIN(Notify
)
519 JACK_DBUS_METHOD_ARGUMENT("queue", DBUS_TYPE_BOOLEAN_AS_STRING
, false)
520 JACK_DBUS_METHOD_ARGUMENT("target", DBUS_TYPE_STRING_AS_STRING
, false)
521 JACK_DBUS_METHOD_ARGUMENT("type", DBUS_TYPE_UINT32_AS_STRING
, false)
522 JACK_DBUS_METHOD_ARGUMENT("path", DBUS_TYPE_STRING_AS_STRING
, false)
523 JACK_DBUS_METHOD_ARGUMENT("result", "a(sssu)", true)
524 JACK_DBUS_METHOD_ARGUMENTS_END
526 JACK_DBUS_METHOD_ARGUMENTS_BEGIN(GetUuidForClientName
)
527 JACK_DBUS_METHOD_ARGUMENT("name", DBUS_TYPE_STRING_AS_STRING
, false)
528 JACK_DBUS_METHOD_ARGUMENT("uuid", DBUS_TYPE_STRING_AS_STRING
, true)
529 JACK_DBUS_METHOD_ARGUMENTS_END
531 JACK_DBUS_METHOD_ARGUMENTS_BEGIN(GetClientNameByUuid
)
532 JACK_DBUS_METHOD_ARGUMENT("uuid", DBUS_TYPE_STRING_AS_STRING
, false)
533 JACK_DBUS_METHOD_ARGUMENT("name", DBUS_TYPE_STRING_AS_STRING
, true)
534 JACK_DBUS_METHOD_ARGUMENTS_END
536 JACK_DBUS_METHOD_ARGUMENTS_BEGIN(ReserveClientName
)
537 JACK_DBUS_METHOD_ARGUMENT("name", DBUS_TYPE_STRING_AS_STRING
, false)
538 JACK_DBUS_METHOD_ARGUMENT("uuid", DBUS_TYPE_STRING_AS_STRING
, false)
539 JACK_DBUS_METHOD_ARGUMENTS_END
541 JACK_DBUS_METHOD_ARGUMENTS_BEGIN(HasSessionCallback
)
542 JACK_DBUS_METHOD_ARGUMENT("client_name", DBUS_TYPE_STRING_AS_STRING
, false)
543 JACK_DBUS_METHOD_ARGUMENT("has_session_callback", DBUS_TYPE_BOOLEAN_AS_STRING
, true)
544 JACK_DBUS_METHOD_ARGUMENTS_END
546 JACK_DBUS_METHOD_ARGUMENTS_BEGIN(GetState
)
547 JACK_DBUS_METHOD_ARGUMENT("type", DBUS_TYPE_UINT32_AS_STRING
, true)
548 JACK_DBUS_METHOD_ARGUMENT("target", DBUS_TYPE_STRING_AS_STRING
, true)
549 JACK_DBUS_METHOD_ARGUMENTS_END
551 JACK_DBUS_SIGNAL_ARGUMENTS_BEGIN(StateChanged
)
552 JACK_DBUS_SIGNAL_ARGUMENT("type", DBUS_TYPE_UINT32_AS_STRING
)
553 JACK_DBUS_SIGNAL_ARGUMENT("target", DBUS_TYPE_STRING_AS_STRING
)
554 JACK_DBUS_SIGNAL_ARGUMENTS_END
556 JACK_DBUS_METHODS_BEGIN
557 JACK_DBUS_METHOD_DESCRIBE(Notify
, jack_controller_dbus_session_notify
)
558 JACK_DBUS_METHOD_DESCRIBE(GetUuidForClientName
, jack_controller_dbus_get_uuid_for_client_name
)
559 JACK_DBUS_METHOD_DESCRIBE(GetClientNameByUuid
, jack_controller_dbus_get_client_name_by_uuid
)
560 JACK_DBUS_METHOD_DESCRIBE(ReserveClientName
, jack_controller_dbus_reserve_client_name
)
561 JACK_DBUS_METHOD_DESCRIBE(HasSessionCallback
, jack_controller_dbus_has_session_callback
)
562 JACK_DBUS_METHOD_DESCRIBE(GetState
, jack_controller_dbus_get_session_state
)
563 JACK_DBUS_METHODS_END
565 JACK_DBUS_SIGNALS_BEGIN
566 JACK_DBUS_SIGNAL_DESCRIBE(StateChanged
)
567 JACK_DBUS_SIGNALS_END
569 JACK_DBUS_IFACE_BEGIN(g_jack_controller_iface_session_manager
, JACK_DBUS_IFACE_NAME
)
570 JACK_DBUS_IFACE_EXPOSE_METHODS
571 JACK_DBUS_IFACE_EXPOSE_SIGNALS