NEWS.adoc: [Post-factum] Update for jackdbus 2.23.0 (2023-10-26)
[jackdbus.git] / dbus / controller_iface_session_manager.c
blob78ace2efa42a501e99d45b4fdee3213bc57768ca
1 /* -*- Mode: C ; c-basic-offset: 4 -*- */
2 /*
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)
21 #include "config.h"
22 #endif
24 #include <stdint.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <assert.h>
28 #include <dbus/dbus.h>
30 #include "jackdbus.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"
37 static
38 void
39 jack_controller_control_send_signal_session_state_changed(
40 jack_session_event_type_t type,
41 const char * target)
43 dbus_uint32_t u32;
45 u32 = type;
46 if (target == NULL)
48 target = "";
51 jack_dbus_send_signal(
52 JACK_CONTROLLER_OBJECT_PATH,
53 JACK_DBUS_IFACE_NAME,
54 "StateChanged",
55 DBUS_TYPE_UINT32,
56 &u32,
57 DBUS_TYPE_STRING,
58 &target,
59 DBUS_TYPE_INVALID);
62 static bool start_detached_thread(void * (* start_routine)(void *), void * arg)
64 int ret;
65 static pthread_attr_t attr;
66 pthread_t tid;
68 ret = pthread_attr_init(&attr);
69 if (ret != 0)
71 jack_error("pthread_attr_init() failed with %d", ret);
72 goto exit;
75 ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
76 if (ret != 0)
78 jack_error("pthread_attr_setdetachstate() failed with %d", ret);
79 goto destroy_attr;
82 ret = pthread_create(&tid, &attr, start_routine, arg);
83 if (ret != 0)
85 jack_error("pthread_create() failed with %d", ret);
86 goto destroy_attr;
89 jack_log("Detached thread %d created", (int)tid);
91 destroy_attr:
92 pthread_attr_destroy(&attr);
93 exit:
94 return ret == 0;
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;
102 dbus_uint32_t u32;
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");
111 goto send_reply;
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)
119 goto oom;
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))
126 goto unref;
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))
133 goto close_array;
136 if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &cmd_ptr->uuid))
138 goto close_struct;
141 if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &cmd_ptr->client_name))
143 goto close_struct;
146 if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &cmd_ptr->command))
148 goto close_struct;
151 u32 = cmd_ptr->flags;
152 if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT32, &u32))
154 goto close_struct;
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))
161 goto close_array;
165 jack_info("End of session commands.");
167 if (!dbus_message_iter_close_container(&top_iter, &array_iter))
169 goto unref;
172 goto send_reply;
174 close_struct:
175 dbus_message_iter_close_container(&array_iter, &struct_iter);
176 close_array:
177 dbus_message_iter_close_container(&top_iter, &array_iter);
178 unref:
179 dbus_message_unref(call.reply);
180 goto oom;
182 send_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);
193 else
195 oom:
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);
209 loop:
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))
245 goto loop;
248 pthread_mutex_unlock(&controller_ptr->lock);
250 jack_log("jack_controller_process_session_command_thread exit");
251 return NULL;
254 #undef controller_ptr
255 #define controller_ptr ((struct jack_controller *)call->context)
257 static
258 void
259 jack_controller_dbus_session_notify(
260 struct jack_dbus_method_call * call)
262 dbus_bool_t queue;
263 const char * target;
264 dbus_uint32_t u32;
265 const char * path;
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);
272 return;
275 if (!jack_dbus_get_method_args(
276 call,
277 DBUS_TYPE_BOOLEAN,
278 &queue,
279 DBUS_TYPE_STRING,
280 &target,
281 DBUS_TYPE_UINT32,
282 &u32,
283 DBUS_TYPE_STRING,
284 &path,
285 DBUS_TYPE_INVALID))
287 /* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */
288 return;
291 if (*target == 0)
293 target = NULL;
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);
303 return;
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");
312 goto unlock;
315 jack_log("Session notify thread started");
317 else if (!queue)
319 jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "Busy");
320 goto unlock;
323 cmd_ptr = malloc(sizeof(struct jack_session_pending_command));
324 if (cmd_ptr == NULL)
326 jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "malloc() failed for jack_session_pending_command struct");
327 goto unlock;
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);
343 unlock:
344 pthread_mutex_unlock(&controller_ptr->lock);
347 static
348 void
349 jack_controller_dbus_get_uuid_for_client_name(
350 struct jack_dbus_method_call * call)
352 const char * client_name;
353 char * client_uuid;
355 if (!jack_dbus_get_method_args(
356 call,
357 DBUS_TYPE_STRING,
358 &client_name,
359 DBUS_TYPE_INVALID))
361 /* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */
362 return;
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);
369 return;
372 jack_dbus_construct_method_return_single(call, DBUS_TYPE_STRING, (message_arg_t)(const char *)client_uuid);
373 free(client_uuid);
376 static
377 void
378 jack_controller_dbus_get_client_name_by_uuid(
379 struct jack_dbus_method_call * call)
381 const char * client_uuid;
382 char * client_name;
384 if (!jack_dbus_get_method_args(
385 call,
386 DBUS_TYPE_STRING,
387 &client_uuid,
388 DBUS_TYPE_INVALID))
390 /* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */
391 return;
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);
398 return;
401 jack_dbus_construct_method_return_single(call, DBUS_TYPE_STRING, (message_arg_t)(const char *)client_name);
402 free(client_name);
405 static
406 void
407 jack_controller_dbus_reserve_client_name(
408 struct jack_dbus_method_call * call)
410 int ret;
411 const char * client_name;
412 const char * client_uuid;
414 if (!jack_dbus_get_method_args(
415 call,
416 DBUS_TYPE_STRING,
417 &client_name,
418 DBUS_TYPE_STRING,
419 &client_uuid,
420 DBUS_TYPE_INVALID))
422 /* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */
423 return;
426 ret = jack_reserve_client_name(controller_ptr->client, client_name, client_uuid);
427 if (ret < 0)
429 jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "jack_reserve_client_name(name=\"%s\", uuid=\"%s\") failed (%d)", client_name, client_uuid, ret);
430 return;
433 jack_dbus_construct_method_return_empty(call);
436 static
437 void
438 jack_controller_dbus_has_session_callback(
439 struct jack_dbus_method_call * call)
441 int ret;
442 const char * client_name;
443 message_arg_t retval;
445 if (!jack_dbus_get_method_args(
446 call,
447 DBUS_TYPE_STRING,
448 &client_name,
449 DBUS_TYPE_INVALID))
451 /* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */
452 return;
455 ret = jack_client_has_session_callback(controller_ptr->client, client_name);
456 if (ret < 0)
458 jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "jack_client_has_session_callback(\"%s\") failed (%d)", client_name, ret);
459 return;
462 retval.boolean = ret;
463 jack_dbus_construct_method_return_single(call, DBUS_TYPE_BOOLEAN, retval);
466 static
467 void
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;
473 const char * target;
474 dbus_uint32_t type;
475 bool append_failed;
477 call->reply = dbus_message_new_method_return(call->message);
478 if (call->reply == NULL)
480 goto oom;
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))
489 type = 0;
490 target = "";
492 else
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;
499 append_failed =
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);
505 if (!append_failed)
507 return;
510 dbus_message_unref(call->reply);
511 call->reply = NULL;
512 oom:
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
572 JACK_DBUS_IFACE_END