Merge branch 'stable' into 'main'
[ladish.git] / daemon / studio.c
blobc336db7bff7d658463f3c44a75d7a70b3a4bd4d2
1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
2 /*
3 * LADI Session Handler (ladish)
5 * Copyright (C) 2009, 2010, 2011, 2012 Nedko Arnaudov <nedko@arnaudov.name>
7 **************************************************************************
8 * This file contains part of the studio singleton object implementation
9 * Other parts are in the other studio*.c files in same directory.
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.h"
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <unistd.h>
34 #include "studio_internal.h"
35 #include "../dbus_constants.h"
36 #include "control.h"
37 #include "../common/catdup.h"
38 #include "../common/dirhelpers.h"
39 #include "graph_dict.h"
40 #include "graph_manager.h"
41 #include "escape.h"
42 #include "studio.h"
43 #include "../proxies/notify_proxy.h"
45 #define STUDIOS_DIR "/studios/"
47 #define RECENT_STUDIOS_STORE_FILE "recent_studios"
48 #define RECENT_STUDIOS_STORE_MAX_ITEMS 50
50 char * g_studios_dir;
51 ladish_recent_store_handle g_studios_recent_store;
53 struct studio g_studio;
55 bool ladish_studio_name_generate(char ** name_ptr)
57 time_t now;
58 char timestamp_str[26];
59 char * name;
61 time(&now);
62 //ctime_r(&now, timestamp_str);
63 //timestamp_str[24] = 0;
64 snprintf(timestamp_str, sizeof(timestamp_str), "%llu", (unsigned long long)now);
66 name = catdup("Studio ", timestamp_str);
67 if (name == NULL)
69 log_error("catdup failed to create studio name");
70 return false;
73 *name_ptr = name;
74 return true;
77 bool ladish_studio_show(void)
79 cdbus_object_path object;
81 ASSERT(g_studio.name != NULL);
82 ASSERT(!g_studio.announced);
84 object = cdbus_object_path_new(
85 STUDIO_OBJECT_PATH,
86 &g_interface_studio, &g_studio,
87 &g_interface_patchbay, ladish_graph_get_dbus_context(g_studio.studio_graph),
88 &g_iface_graph_dict, g_studio.studio_graph,
89 &g_iface_graph_manager, g_studio.studio_graph,
90 &g_iface_app_supervisor, g_studio.app_supervisor,
91 NULL);
92 if (object == NULL)
94 log_error("cdbus_object_path_new() failed");
95 return false;
98 if (!cdbus_object_path_register(cdbus_g_dbus_connection, object))
100 log_error("object_path_register() failed");
101 cdbus_object_path_destroy(cdbus_g_dbus_connection, object);
102 return false;
105 log_info("Studio D-Bus object created. \"%s\"", g_studio.name);
107 g_studio.dbus_object = object;
109 emit_studio_appeared();
111 return true;
114 void ladish_studio_announce(void)
116 ASSERT(!g_studio.announced);
118 /* notify the user that studio started successfully, but dont lie when jack was started externally */
119 if (!g_studio.automatic)
121 ladish_notify_simple(LADISH_NOTIFY_URGENCY_NORMAL, "Studio loaded", NULL);
124 g_studio.announced = true;
127 bool ladish_studio_publish(void)
129 if (!ladish_studio_show())
131 return false;
134 ladish_studio_announce();
135 return true;
138 void ladish_studio_clear(void)
140 ladish_graph_dump(g_studio.studio_graph);
141 ladish_graph_dump(g_studio.jack_graph);
143 /* remove rooms that own clients in studio graph before clearing it */
144 ladish_studio_remove_all_rooms();
146 ladish_graph_clear(g_studio.studio_graph, NULL);
147 ladish_graph_clear(g_studio.jack_graph, NULL);
149 ladish_studio_jack_conf_clear();
151 g_studio.modified = false;
152 g_studio.persisted = false;
154 ladish_app_supervisor_clear(g_studio.app_supervisor);
156 if (g_studio.dbus_object != NULL)
158 cdbus_object_path_destroy(cdbus_g_dbus_connection, g_studio.dbus_object);
159 g_studio.dbus_object = NULL;
160 emit_studio_disappeared();
163 if (g_studio.announced)
165 ladish_notify_simple(LADISH_NOTIFY_URGENCY_NORMAL, "Studio unloaded", NULL);
166 g_studio.announced = false;
169 if (g_studio.name != NULL)
171 free(g_studio.name);
172 g_studio.name = NULL;
175 if (g_studio.filename != NULL)
177 free(g_studio.filename);
178 g_studio.filename = NULL;
182 void ladish_studio_emit_started(void)
184 cdbus_signal_emit(cdbus_g_dbus_connection, STUDIO_OBJECT_PATH, IFACE_STUDIO, "StudioStarted", "");
187 void ladish_studio_emit_crashed(void)
189 cdbus_signal_emit(cdbus_g_dbus_connection, STUDIO_OBJECT_PATH, IFACE_STUDIO, "StudioCrashed", "");
192 void ladish_studio_emit_stopped(void)
194 cdbus_signal_emit(cdbus_g_dbus_connection, STUDIO_OBJECT_PATH, IFACE_STUDIO, "StudioStopped", "");
197 static bool ladish_studio_fill_room_info(DBusMessageIter * iter_ptr, ladish_room_handle room)
199 DBusMessageIter dict_iter;
200 const char * name;
201 uuid_t template_uuid;
202 ladish_room_handle template;
203 const char * template_name;
204 const char * opath;
206 name = ladish_room_get_name(room);
207 opath = ladish_room_get_opath(room);
209 if (!ladish_room_get_template_uuid(room, template_uuid))
211 template = NULL;
212 template_name = NULL;
214 else
216 template = find_room_template_by_uuid(template_uuid);
217 if (template != NULL)
219 template_name = ladish_room_get_name(template);
221 else
223 template_name = NULL;
227 if (!dbus_message_iter_append_basic(iter_ptr, DBUS_TYPE_STRING, &opath))
229 log_error("dbus_message_iter_append_basic() failed.");
230 return false;
233 if (!dbus_message_iter_open_container(iter_ptr, DBUS_TYPE_ARRAY, "{sv}", &dict_iter))
235 log_error("dbus_message_iter_open_container() failed.");
236 return false;
239 if (!cdbus_maybe_add_dict_entry_string(&dict_iter, "template", template_name))
241 log_error("dbus_maybe_add_dict_entry_string() failed.");
242 return false;
245 if (!cdbus_maybe_add_dict_entry_string(&dict_iter, "name", name))
247 log_error("dbus_maybe_add_dict_entry_string() failed.");
248 return false;
251 if (!dbus_message_iter_close_container(iter_ptr, &dict_iter))
253 log_error("dbus_message_iter_close_container() failed.");
254 return false;
257 return true;
260 static void ladish_studio_emit_room_appeared(ladish_room_handle room)
262 DBusMessage * message_ptr;
263 DBusMessageIter iter;
265 message_ptr = dbus_message_new_signal(STUDIO_OBJECT_PATH, IFACE_STUDIO, "RoomAppeared");
266 if (message_ptr == NULL)
268 log_error("dbus_message_new_signal() failed.");
269 return;
272 dbus_message_iter_init_append(message_ptr, &iter);
274 if (ladish_studio_fill_room_info(&iter, room))
276 cdbus_signal_send(cdbus_g_dbus_connection, message_ptr);
279 dbus_message_unref(message_ptr);
282 void ladish_studio_emit_room_disappeared(ladish_room_handle room)
284 DBusMessage * message_ptr;
285 DBusMessageIter iter;
287 message_ptr = dbus_message_new_signal(STUDIO_OBJECT_PATH, IFACE_STUDIO, "RoomDisappeared");
288 if (message_ptr == NULL)
290 log_error("dbus_message_new_signal() failed.");
291 return;
294 dbus_message_iter_init_append(message_ptr, &iter);
296 if (ladish_studio_fill_room_info(&iter, room))
298 cdbus_signal_send(cdbus_g_dbus_connection, message_ptr);
301 dbus_message_unref(message_ptr);
304 void ladish_studio_room_appeared(ladish_room_handle room)
306 log_info("Room \"%s\" appeared", ladish_room_get_name(room));
307 list_add_tail(ladish_room_get_list_node(room), &g_studio.rooms);
308 ladish_studio_emit_room_appeared(room);
311 void ladish_studio_room_disappeared(ladish_room_handle room)
313 log_info("Room \"%s\" disappeared", ladish_room_get_name(room));
314 list_del(ladish_room_get_list_node(room));
315 ladish_studio_emit_room_disappeared(room);
318 bool
319 ladish_studio_set_graph_connection_handlers(
320 void * context,
321 ladish_graph_handle graph,
322 ladish_app_supervisor_handle UNUSED(app_supervisor))
324 ladish_virtualizer_set_graph_connection_handlers(context, graph);
325 return true; /* iterate all graphs */
328 void ladish_studio_on_event_jack_started(void)
330 if (!ladish_studio_fetch_jack_settings())
332 log_error("studio_fetch_jack_settings() failed.");
334 return;
337 log_info("jack conf successfully retrieved");
338 g_studio.jack_conf_valid = true;
340 if (!graph_proxy_create(JACKDBUS_SERVICE_NAME, JACKDBUS_OBJECT_PATH, false, false, &g_studio.jack_graph_proxy))
342 log_error("graph_proxy_create() failed for jackdbus");
344 else
346 if (!ladish_virtualizer_create(g_studio.jack_graph_proxy, g_studio.jack_graph, &g_studio.virtualizer))
348 log_error("ladish_virtualizer_create() failed.");
350 else
352 ladish_studio_iterate_virtual_graphs(g_studio.virtualizer, ladish_studio_set_graph_connection_handlers);
355 if (!graph_proxy_activate(g_studio.jack_graph_proxy))
357 log_error("graph_proxy_activate() failed.");
361 ladish_app_supervisor_autorun(g_studio.app_supervisor);
363 ladish_studio_emit_started();
365 /* notify the user that studio started successfully, but dont lie when jack was started externally */
366 if (!g_studio.automatic)
368 ladish_notify_simple(LADISH_NOTIFY_URGENCY_NORMAL, "Studio started", NULL);
372 static void ladish_studio_on_jack_stopped_internal(void)
374 if (g_studio.virtualizer)
376 ladish_virtualizer_destroy(g_studio.virtualizer);
377 g_studio.virtualizer = NULL;
380 if (g_studio.jack_graph_proxy)
382 graph_proxy_destroy(g_studio.jack_graph_proxy);
383 g_studio.jack_graph_proxy = NULL;
387 void ladish_studio_on_event_jack_stopped(void)
389 ladish_studio_emit_stopped();
390 ladish_studio_on_jack_stopped_internal();
391 ladish_notify_simple(LADISH_NOTIFY_URGENCY_NORMAL, "Studio stopped", NULL);
394 void ladish_studio_handle_unexpected_jack_server_stop(void)
396 ladish_studio_emit_crashed();
397 ladish_studio_on_jack_stopped_internal();
399 /* TODO: if user wants, restart jack server and reconnect all jack apps to it */
402 static
403 bool
404 ladish_studio_hide_vgraph_non_virtual(
405 void * UNUSED(context),
406 ladish_graph_handle graph,
407 ladish_app_supervisor_handle UNUSED(app_supervisor))
409 ladish_graph_hide_non_virtual(graph);
410 return true; /* iterate all vgraphs */
413 void ladish_studio_run(void)
415 bool state;
417 ladish_cqueue_run(&g_studio.cmd_queue);
418 if (g_quit)
419 { /* if quit is requested, don't bother to process external events */
420 return;
423 if (ladish_environment_consume_change(&g_studio.env_store, ladish_environment_jack_server_started, &state))
425 ladish_cqueue_clear(&g_studio.cmd_queue);
427 if (state)
429 ladish_environment_ignore(&g_studio.env_store, ladish_environment_jack_server_present);
431 if (g_studio.jack_graph_proxy != NULL)
433 log_error("Ignoring \"JACK started\" notification because it is already known that JACK is currently started.");
434 return;
437 /* Automatic studio creation on JACK server start */
438 if (g_studio.dbus_object == NULL)
440 ASSERT(g_studio.name == NULL);
441 if (!ladish_studio_name_generate(&g_studio.name))
443 log_error("studio_name_generate() failed.");
444 return;
447 g_studio.automatic = true;
449 ladish_studio_publish();
452 ladish_studio_on_event_jack_started();
454 if (g_studio.automatic)
456 ladish_notify_simple(
457 LADISH_NOTIFY_URGENCY_NORMAL,
458 "Automatic studio created",
459 "JACK server is started not by ladish daemon and there is no loaded studio, so a new studio is created and marked as started.");
462 else
464 /* JACK stopped but this was not expected. When expected.
465 * the change will be consumed by the run method of the studio stop command */
467 if (g_studio.jack_graph_proxy == NULL)
469 log_error("Ignoring \"JACK stopped\" notification because it is already known that JACK is currently stopped.");
470 return;
473 if (g_studio.automatic)
475 log_info("Unloading automatic studio.");
476 ladish_command_unload_studio(NULL, &g_studio.cmd_queue);
478 ladish_studio_on_event_jack_stopped();
479 return;
482 log_error("JACK stopped unexpectedly.");
483 log_error("Save your work, then unload and reload the studio.");
484 ladish_notify_simple(
485 LADISH_NOTIFY_URGENCY_HIGH,
486 "Studio crashed",
487 "JACK stopped unexpectedly.\n\n"
488 "Save your work, then unload and reload the studio.");
489 ladish_studio_handle_unexpected_jack_server_stop();
493 if (ladish_environment_consume_change(&g_studio.env_store, ladish_environment_jack_server_present, &state))
495 if (g_studio.jack_graph_proxy != NULL)
497 ladish_cqueue_clear(&g_studio.cmd_queue);
499 /* jack was started, this probably means that jackdbus has crashed */
500 log_error("JACK disappeared unexpectedly. Maybe it crashed.");
501 log_error("Save your work, then unload and reload the studio.");
502 ladish_notify_simple(
503 LADISH_NOTIFY_URGENCY_HIGH,
504 "Studio crashed",
505 "JACK disappeared unexpectedly. Maybe it crashed.\n\n"
506 "Save your work, then unload and reload the studio.");
507 ladish_environment_reset_stealth(&g_studio.env_store, ladish_environment_jack_server_started);
509 ladish_studio_iterate_virtual_graphs(NULL, ladish_studio_hide_vgraph_non_virtual);
511 ladish_studio_handle_unexpected_jack_server_stop();
515 ladish_environment_assert_consumed(&g_studio.env_store);
518 static void ladish_studio_on_jack_server_started(void)
520 log_info("JACK server start detected.");
521 ladish_environment_set(&g_studio.env_store, ladish_environment_jack_server_started);
524 static void ladish_studio_on_jack_server_stopped(void)
526 log_info("JACK server stop detected.");
527 ladish_environment_reset(&g_studio.env_store, ladish_environment_jack_server_started);
530 static void ladish_studio_on_jack_server_appeared(void)
532 log_info("JACK controller appeared.");
533 ladish_environment_set(&g_studio.env_store, ladish_environment_jack_server_present);
536 static void ladish_studio_on_jack_server_disappeared(void)
538 log_info("JACK controller disappeared.");
539 ladish_environment_reset(&g_studio.env_store, ladish_environment_jack_server_present);
542 bool ladish_studio_init(void)
544 char * studios_recent_store_path;
546 log_info("studio object construct");
548 g_studios_dir = catdup(g_base_dir, STUDIOS_DIR);
549 if (g_studios_dir == NULL)
551 log_error("catdup failed for '%s' and '%s'", g_base_dir, STUDIOS_DIR);
552 goto fail;
555 if (!ensure_dir_exist(g_studios_dir, 0700))
557 goto free_studios_dir;
560 studios_recent_store_path = catdup(g_base_dir, "/" RECENT_STUDIOS_STORE_FILE);
561 if (studios_recent_store_path == NULL)
563 log_error("catdup failed for to compose recent studios store file path");
564 goto free_studios_dir;
567 if (!ladish_recent_store_create(studios_recent_store_path, 10, &g_studios_recent_store))
569 free(studios_recent_store_path);
570 goto free_studios_dir;
573 free(studios_recent_store_path);
575 INIT_LIST_HEAD(&g_studio.all_connections);
576 INIT_LIST_HEAD(&g_studio.all_ports);
577 INIT_LIST_HEAD(&g_studio.all_clients);
578 INIT_LIST_HEAD(&g_studio.jack_connections);
579 INIT_LIST_HEAD(&g_studio.jack_ports);
580 INIT_LIST_HEAD(&g_studio.jack_clients);
581 INIT_LIST_HEAD(&g_studio.rooms);
582 INIT_LIST_HEAD(&g_studio.clients);
583 INIT_LIST_HEAD(&g_studio.ports);
585 INIT_LIST_HEAD(&g_studio.jack_conf);
586 INIT_LIST_HEAD(&g_studio.jack_params);
588 g_studio.dbus_object = NULL;
589 g_studio.announced = false;
590 g_studio.name = NULL;
591 g_studio.filename = NULL;
593 g_studio.room_count = 0;
595 if (!ladish_graph_create(&g_studio.jack_graph, NULL))
597 log_error("ladish_graph_create() failed to create jack graph object.");
598 goto destroy_recent_store;
601 if (!ladish_graph_create(&g_studio.studio_graph, STUDIO_OBJECT_PATH))
603 log_error("ladish_graph_create() failed to create studio graph object.");
604 goto jack_graph_destroy;
607 if (!ladish_app_supervisor_create(&g_studio.app_supervisor, STUDIO_OBJECT_PATH, "studio", g_studio.studio_graph, ladish_virtualizer_rename_app))
609 log_error("ladish_app_supervisor_create() failed.");
610 goto studio_graph_destroy;
613 ladish_cqueue_init(&g_studio.cmd_queue);
614 ladish_environment_init(&g_studio.env_store);
616 if (!jack_proxy_init(
617 ladish_studio_on_jack_server_started,
618 ladish_studio_on_jack_server_stopped,
619 ladish_studio_on_jack_server_appeared,
620 ladish_studio_on_jack_server_disappeared))
622 log_error("jack_proxy_init() failed.");
623 goto app_supervisor_destroy;
626 return true;
628 app_supervisor_destroy:
629 ladish_app_supervisor_destroy(g_studio.app_supervisor);
630 studio_graph_destroy:
631 ladish_graph_destroy(g_studio.studio_graph);
632 jack_graph_destroy:
633 ladish_graph_destroy(g_studio.jack_graph);
634 destroy_recent_store:
635 ladish_recent_store_destroy(g_studios_recent_store);
636 free_studios_dir:
637 free(g_studios_dir);
638 fail:
639 return false;
642 void ladish_studio_uninit(void)
644 log_info("studio_uninit()");
646 jack_proxy_uninit();
648 ladish_cqueue_clear(&g_studio.cmd_queue);
650 ladish_graph_destroy(g_studio.studio_graph);
651 ladish_graph_destroy(g_studio.jack_graph);
653 ladish_recent_store_destroy(g_studios_recent_store);
655 free(g_studios_dir);
657 log_info("studio object destroy");
660 struct on_child_exit_context
662 pid_t pid;
663 int exit_status;
664 bool found;
667 #define child_exit_context_ptr ((struct on_child_exit_context *)context)
669 static
670 bool
671 ladish_studio_on_child_exit_callback(
672 void * context,
673 ladish_graph_handle UNUSED(graph),
674 ladish_app_supervisor_handle app_supervisor)
676 child_exit_context_ptr->found = ladish_app_supervisor_child_exit(app_supervisor, child_exit_context_ptr->pid, child_exit_context_ptr->exit_status);
677 /* if child is found, return false - it will cause iteration to stop */
678 /* if child is not found, return true - it will cause next supervisor to be checked */
679 return !child_exit_context_ptr->found;
682 #undef child_exit_context_ptr
684 void ladish_studio_on_child_exit(pid_t pid, int exit_status)
686 struct on_child_exit_context context;
688 context.pid = pid;
689 context.exit_status = exit_status;
690 context.found = false;
692 ladish_studio_iterate_virtual_graphs(&context, ladish_studio_on_child_exit_callback);
694 if (!context.found)
696 log_error("unknown child exit detected. pid is %llu", (unsigned long long)pid);
700 bool ladish_studio_is_loaded(void)
702 return g_studio.dbus_object != NULL && g_studio.announced;
705 bool ladish_studio_is_started(void)
707 return ladish_environment_get(&g_studio.env_store, ladish_environment_jack_server_started);
710 bool ladish_studio_compose_filename(const char * name, char ** filename_ptr_ptr, char ** backup_filename_ptr_ptr)
712 size_t len_dir;
713 char * p;
714 const char * src;
715 char * filename_ptr;
716 char * backup_filename_ptr = NULL;
718 len_dir = strlen(g_studios_dir);
720 filename_ptr = malloc(len_dir + 1 + strlen(name) * 3 + 4 + 1);
721 if (filename_ptr == NULL)
723 log_error("malloc failed to allocate memory for studio file path");
724 return false;
727 if (backup_filename_ptr_ptr != NULL)
729 backup_filename_ptr = malloc(len_dir + 1 + strlen(name) * 3 + 4 + 4 + 1);
730 if (backup_filename_ptr == NULL)
732 log_error("malloc failed to allocate memory for studio backup file path");
733 free(filename_ptr);
734 return false;
738 p = filename_ptr;
739 memcpy(p, g_studios_dir, len_dir);
740 p += len_dir;
742 *p++ = '/';
744 src = name;
745 escape(&src, &p, LADISH_ESCAPE_FLAG_ALL);
746 strcpy(p, ".xml");
748 *filename_ptr_ptr = filename_ptr;
750 if (backup_filename_ptr_ptr != NULL)
752 strcpy(backup_filename_ptr, filename_ptr);
753 strcat(backup_filename_ptr, ".bak");
754 *backup_filename_ptr_ptr = backup_filename_ptr;
757 return true;
760 struct ladish_cqueue * ladish_studio_get_cmd_queue(void)
762 return &g_studio.cmd_queue;
765 ladish_virtualizer_handle ladish_studio_get_virtualizer(void)
767 return g_studio.virtualizer;
770 ladish_graph_handle ladish_studio_get_jack_graph(void)
772 return g_studio.jack_graph;
775 ladish_graph_handle ladish_studio_get_studio_graph(void)
777 return g_studio.studio_graph;
780 ladish_app_supervisor_handle ladish_studio_get_studio_app_supervisor(void)
782 return g_studio.app_supervisor;
785 struct ladish_studio_app_supervisor_match_opath_context
787 const char * opath;
788 ladish_app_supervisor_handle supervisor;
791 #define iterate_context_ptr ((struct ladish_studio_app_supervisor_match_opath_context *)context)
793 static bool ladish_studio_app_supervisor_match_opath(void * context, ladish_graph_handle graph, ladish_app_supervisor_handle app_supervisor)
795 ASSERT(strcmp(ladish_app_supervisor_get_opath(app_supervisor), ladish_graph_get_opath(graph)) == 0);
796 if (strcmp(ladish_app_supervisor_get_opath(app_supervisor), iterate_context_ptr->opath) == 0)
798 ASSERT(iterate_context_ptr->supervisor == NULL);
799 iterate_context_ptr->supervisor = app_supervisor;
800 return false; /* stop iteration */
803 return true; /* continue iteration */
806 #undef iterate_context_ptr
808 ladish_app_supervisor_handle ladish_studio_find_app_supervisor(const char * opath)
810 struct ladish_studio_app_supervisor_match_opath_context ctx;
812 ctx.opath = opath;
813 ctx.supervisor = NULL;
814 ladish_studio_iterate_virtual_graphs(&ctx, ladish_studio_app_supervisor_match_opath);
815 return ctx.supervisor;
818 struct ladish_studio_app_supervisor_match_app_context
820 uuid_t app_uuid;
821 ladish_app_handle app;
824 #define iterate_context_ptr ((struct ladish_studio_app_supervisor_match_app_context *)context)
826 static bool ladish_studio_app_supervisor_match_app(void * context, ladish_graph_handle graph, ladish_app_supervisor_handle app_supervisor)
828 ASSERT(strcmp(ladish_app_supervisor_get_opath(app_supervisor), ladish_graph_get_opath(graph)) == 0);
829 ASSERT(iterate_context_ptr->app == NULL);
831 iterate_context_ptr->app = ladish_app_supervisor_find_app_by_uuid(app_supervisor, iterate_context_ptr->app_uuid);
832 if (iterate_context_ptr->app != NULL)
834 return false; /* stop iteration */
837 return true; /* continue iteration */
840 #undef iterate_context_ptr
842 ladish_app_handle ladish_studio_find_app_by_uuid(const uuid_t app_uuid)
844 struct ladish_studio_app_supervisor_match_app_context ctx;
846 uuid_copy(ctx.app_uuid, app_uuid);
847 ctx.app = NULL;
848 ladish_studio_iterate_virtual_graphs(&ctx, ladish_studio_app_supervisor_match_app);
849 return ctx.app;
852 bool ladish_studio_delete(void * call_ptr, const char * studio_name)
854 char * filename;
855 char * bak_filename;
856 struct stat st;
857 bool ret;
859 ret = false;
861 if (!ladish_studio_compose_filename(studio_name, &filename, &bak_filename))
863 cdbus_error(call_ptr, DBUS_ERROR_FAILED, "failed to compose studio filename");
864 goto exit;
867 log_info("Deleting studio ('%s')", filename);
869 if (unlink(filename) != 0)
871 cdbus_error(call_ptr, DBUS_ERROR_FAILED, "unlink(%s) failed: %d (%s)", filename, errno, strerror(errno));
872 goto free;
875 /* try to delete the backup file */
876 if (stat(bak_filename, &st) == 0)
878 if (unlink(bak_filename) != 0)
880 /* failing to delete backup file will not case delete command failure */
881 log_error("unlink(%s) failed: %d (%s)", bak_filename, errno, strerror(errno));
885 ret = true;
887 free:
888 free(filename);
889 free(bak_filename);
890 exit:
891 return ret;
894 bool
895 ladish_studio_iterate_virtual_graphs(
896 void * context,
897 bool (* callback)(
898 void * context,
899 ladish_graph_handle graph,
900 ladish_app_supervisor_handle app_supervisor))
902 struct list_head * node_ptr;
903 ladish_room_handle room;
904 ladish_app_supervisor_handle room_app_supervisor;
905 ladish_graph_handle room_graph;
907 if (!callback(context, g_studio.studio_graph, g_studio.app_supervisor))
909 return false;
912 list_for_each(node_ptr, &g_studio.rooms)
914 room = ladish_room_from_list_node(node_ptr);
915 room_app_supervisor = ladish_room_get_app_supervisor(room);
916 ASSERT(room_app_supervisor != NULL);
917 room_graph = ladish_room_get_graph(room);
919 if (!callback(context, room_graph, room_app_supervisor))
921 return false;
925 return true;
928 bool
929 ladish_studio_iterate_rooms(
930 void * context,
931 bool (* callback)(
932 void * context,
933 ladish_room_handle room))
935 struct list_head * node_ptr;
936 ladish_room_handle room;
938 list_for_each(node_ptr, &g_studio.rooms)
940 room = ladish_room_from_list_node(node_ptr);
941 if (!callback(context, room))
943 return false;
947 return true;
950 bool ladish_studio_has_rooms(void)
952 return !list_empty(&g_studio.rooms);
955 static
956 bool
957 ladish_studio_stop_app_supervisor(
958 void * UNUSED(context),
959 ladish_graph_handle UNUSED(graph),
960 ladish_app_supervisor_handle app_supervisor)
962 ladish_app_supervisor_stop(app_supervisor);
963 return true; /* iterate all supervisors */
966 void ladish_studio_stop_app_supervisors(void)
968 ladish_studio_iterate_virtual_graphs(NULL, ladish_studio_stop_app_supervisor);
971 void ladish_studio_emit_renamed(void)
973 cdbus_signal_emit(cdbus_g_dbus_connection, STUDIO_OBJECT_PATH, IFACE_STUDIO, "StudioRenamed", "s", &g_studio.name);
976 unsigned int ladish_studio_get_room_index(void)
978 return ++g_studio.room_count;
981 void ladish_studio_release_room_index(unsigned int index)
983 if (index == g_studio.room_count)
985 g_studio.room_count--;
989 void ladish_studio_remove_all_rooms(void)
991 while (!list_empty(&g_studio.rooms))
993 ladish_room_destroy(ladish_room_from_list_node(g_studio.rooms.next));
997 ladish_room_handle ladish_studio_find_room_by_uuid(const uuid_t room_uuid_ptr)
999 struct list_head * node_ptr;
1000 ladish_room_handle room;
1001 uuid_t room_uuid;
1003 list_for_each(node_ptr, &g_studio.rooms)
1005 room = ladish_room_from_list_node(node_ptr);
1006 ladish_room_get_uuid(room, room_uuid);
1007 if (uuid_compare(room_uuid, room_uuid_ptr) == 0)
1009 return room;
1013 return NULL;
1016 bool ladish_studio_check_room_name(const char * room_name)
1018 struct list_head * node_ptr;
1019 ladish_room_handle room;
1021 list_for_each(node_ptr, &g_studio.rooms)
1023 room = ladish_room_from_list_node(node_ptr);
1024 if (strcmp(ladish_room_get_name(room), room_name) == 0)
1026 return true;
1030 return false;
1033 /**********************************************************************************/
1034 /* D-Bus methods */
1035 /**********************************************************************************/
1037 static void ladish_studio_dbus_get_name(struct cdbus_method_call * call_ptr)
1039 cdbus_method_return_new_single(call_ptr, DBUS_TYPE_STRING, &g_studio.name);
1042 static void ladish_studio_dbus_rename(struct cdbus_method_call * call_ptr)
1044 const char * new_name;
1045 char * new_name_dup;
1047 if (!dbus_message_get_args(call_ptr->message, &cdbus_g_dbus_error, DBUS_TYPE_STRING, &new_name, DBUS_TYPE_INVALID))
1049 cdbus_error(call_ptr, DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, cdbus_g_dbus_error.message);
1050 dbus_error_free(&cdbus_g_dbus_error);
1051 return;
1054 log_info("Rename studio request (%s)", new_name);
1056 new_name_dup = strdup(new_name);
1057 if (new_name_dup == NULL)
1059 cdbus_error(call_ptr, DBUS_ERROR_FAILED, "strdup() failed to allocate new name.");
1060 return;
1063 free(g_studio.name);
1064 g_studio.name = new_name_dup;
1066 cdbus_method_return_new_void(call_ptr);
1067 ladish_studio_emit_renamed();
1070 static void ladish_studio_dbus_save(struct cdbus_method_call * call_ptr)
1072 log_info("Save studio request");
1074 if (ladish_command_save_studio(call_ptr, &g_studio.cmd_queue, g_studio.name))
1076 cdbus_method_return_new_void(call_ptr);
1080 static void ladish_studio_dbus_save_as(struct cdbus_method_call * call_ptr)
1082 const char * new_name;
1084 log_info("SaveAs studio request");
1086 if (!dbus_message_get_args(call_ptr->message, &cdbus_g_dbus_error, DBUS_TYPE_STRING, &new_name, DBUS_TYPE_INVALID))
1088 cdbus_error(call_ptr, DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, cdbus_g_dbus_error.message);
1089 dbus_error_free(&cdbus_g_dbus_error);
1090 return;
1093 if (ladish_command_save_studio(call_ptr, &g_studio.cmd_queue, new_name))
1095 cdbus_method_return_new_void(call_ptr);
1099 static void ladish_studio_dbus_unload(struct cdbus_method_call * call_ptr)
1101 log_info("Unload studio request");
1103 if (ladish_command_unload_studio(call_ptr, &g_studio.cmd_queue))
1105 cdbus_method_return_new_void(call_ptr);
1109 static void ladish_studio_dbus_stop(struct cdbus_method_call * call_ptr)
1111 log_info("Stop studio request");
1113 g_studio.automatic = false; /* even if it was automatic, it is not anymore because user knows about it */
1115 if (ladish_command_stop_studio(call_ptr, &g_studio.cmd_queue))
1117 cdbus_method_return_new_void(call_ptr);
1121 static void ladish_studio_dbus_start(struct cdbus_method_call * call_ptr)
1123 log_info("Start studio request");
1125 g_studio.automatic = false; /* even if it was automatic, it is not anymore because user knows about it */
1127 if (ladish_command_start_studio(call_ptr, &g_studio.cmd_queue))
1129 cdbus_method_return_new_void(call_ptr);
1133 static void ladish_studio_dbus_is_started(struct cdbus_method_call * call_ptr)
1135 dbus_bool_t started;
1137 started = g_studio.jack_graph_proxy != NULL;
1139 cdbus_method_return_new_single(call_ptr, DBUS_TYPE_BOOLEAN, &started);
1142 static void ladish_studio_dbus_create_room(struct cdbus_method_call * call_ptr)
1144 const char * room_name;
1145 const char * template_name;
1147 dbus_error_init(&cdbus_g_dbus_error);
1149 if (!dbus_message_get_args(call_ptr->message, &cdbus_g_dbus_error, DBUS_TYPE_STRING, &room_name, DBUS_TYPE_STRING, &template_name, DBUS_TYPE_INVALID))
1151 cdbus_error(call_ptr, DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, cdbus_g_dbus_error.message);
1152 dbus_error_free(&cdbus_g_dbus_error);
1153 return;
1156 if (ladish_command_create_room(call_ptr, ladish_studio_get_cmd_queue(), room_name, template_name))
1158 cdbus_method_return_new_void(call_ptr);
1162 static void ladish_studio_dbus_get_room_list(struct cdbus_method_call * call_ptr)
1164 DBusMessageIter iter, array_iter;
1165 DBusMessageIter struct_iter;
1166 struct list_head * node_ptr;
1167 ladish_room_handle room;
1169 call_ptr->reply = dbus_message_new_method_return(call_ptr->message);
1170 if (call_ptr->reply == NULL)
1172 goto fail;
1175 dbus_message_iter_init_append(call_ptr->reply, &iter);
1177 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sa{sv})", &array_iter))
1179 goto fail_unref;
1182 list_for_each(node_ptr, &g_studio.rooms)
1184 room = ladish_room_from_list_node(node_ptr);
1186 if (!dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter))
1187 goto fail_unref;
1189 if (!ladish_studio_fill_room_info(&struct_iter, room))
1190 goto fail_unref;
1192 if (!dbus_message_iter_close_container(&array_iter, &struct_iter))
1193 goto fail_unref;
1196 if (!dbus_message_iter_close_container(&iter, &array_iter))
1198 goto fail_unref;
1201 return;
1203 fail_unref:
1204 dbus_message_unref(call_ptr->reply);
1205 call_ptr->reply = NULL;
1207 fail:
1208 log_error("Ran out of memory trying to construct method return");
1211 static void ladish_studio_dbus_delete_room(struct cdbus_method_call * call_ptr)
1213 const char * name;
1215 dbus_error_init(&cdbus_g_dbus_error);
1217 if (!dbus_message_get_args(call_ptr->message, &cdbus_g_dbus_error, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID))
1219 cdbus_error(call_ptr, DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, cdbus_g_dbus_error.message);
1220 dbus_error_free(&cdbus_g_dbus_error);
1221 return;
1224 if (ladish_command_delete_room(call_ptr, ladish_studio_get_cmd_queue(), name))
1226 cdbus_method_return_new_void(call_ptr);
1230 CDBUS_METHOD_ARGS_BEGIN(GetName, "Get studio name")
1231 CDBUS_METHOD_ARG_DESCRIBE_OUT("studio_name", "s", "Name of studio")
1232 CDBUS_METHOD_ARGS_END
1234 CDBUS_METHOD_ARGS_BEGIN(Rename, "Rename studio")
1235 CDBUS_METHOD_ARG_DESCRIBE_IN("studio_name", "s", "New name")
1236 CDBUS_METHOD_ARGS_END
1238 CDBUS_METHOD_ARGS_BEGIN(Save, "Save studio")
1239 CDBUS_METHOD_ARGS_END
1241 CDBUS_METHOD_ARGS_BEGIN(SaveAs, "SaveAs studio")
1242 CDBUS_METHOD_ARG_DESCRIBE_IN("studio_name", "s", "New name")
1243 CDBUS_METHOD_ARGS_END
1245 CDBUS_METHOD_ARGS_BEGIN(Unload, "Unload studio")
1246 CDBUS_METHOD_ARGS_END
1248 CDBUS_METHOD_ARGS_BEGIN(Start, "Start studio")
1249 CDBUS_METHOD_ARGS_END
1251 CDBUS_METHOD_ARGS_BEGIN(Stop, "Stop studio")
1252 CDBUS_METHOD_ARGS_END
1254 CDBUS_METHOD_ARGS_BEGIN(IsStarted, "Check whether studio is started")
1255 CDBUS_METHOD_ARG_DESCRIBE_OUT("started", "b", "Whether studio is started")
1256 CDBUS_METHOD_ARGS_END
1258 CDBUS_METHOD_ARGS_BEGIN(CreateRoom, "Create new studio room")
1259 CDBUS_METHOD_ARG_DESCRIBE_IN("room_name", "s", "Studio room name")
1260 CDBUS_METHOD_ARG_DESCRIBE_IN("room_template_name", "s", "Room template name")
1261 CDBUS_METHOD_ARGS_END
1263 CDBUS_METHOD_ARGS_BEGIN(GetRoomList, "Get list of rooms in this studio")
1264 CDBUS_METHOD_ARG_DESCRIBE_OUT("room_list", "a(sa{sv})", "List of studio rooms: opaths and properties")
1265 CDBUS_METHOD_ARGS_END
1267 CDBUS_METHOD_ARGS_BEGIN(DeleteRoom, "Delete studio room")
1268 CDBUS_METHOD_ARG_DESCRIBE_IN("room_name", "s", "Name of studio room to delete")
1269 CDBUS_METHOD_ARGS_END
1271 CDBUS_METHODS_BEGIN
1272 CDBUS_METHOD_DESCRIBE(GetName, ladish_studio_dbus_get_name) /* sync */
1273 CDBUS_METHOD_DESCRIBE(Rename, ladish_studio_dbus_rename) /* sync */
1274 CDBUS_METHOD_DESCRIBE(Save, ladish_studio_dbus_save) /* async */
1275 CDBUS_METHOD_DESCRIBE(SaveAs, ladish_studio_dbus_save_as) /* async */
1276 CDBUS_METHOD_DESCRIBE(Unload, ladish_studio_dbus_unload) /* async */
1277 CDBUS_METHOD_DESCRIBE(Start, ladish_studio_dbus_start) /* async */
1278 CDBUS_METHOD_DESCRIBE(Stop, ladish_studio_dbus_stop) /* async */
1279 CDBUS_METHOD_DESCRIBE(IsStarted, ladish_studio_dbus_is_started) /* sync */
1280 CDBUS_METHOD_DESCRIBE(CreateRoom, ladish_studio_dbus_create_room) /* async */
1281 CDBUS_METHOD_DESCRIBE(GetRoomList, ladish_studio_dbus_get_room_list) /* sync */
1282 CDBUS_METHOD_DESCRIBE(DeleteRoom, ladish_studio_dbus_delete_room) /* async */
1283 CDBUS_METHODS_END
1285 CDBUS_SIGNAL_ARGS_BEGIN(StudioRenamed, "Studio name changed")
1286 CDBUS_SIGNAL_ARG_DESCRIBE("studio_name", "s", "New studio name")
1287 CDBUS_SIGNAL_ARGS_END
1289 CDBUS_SIGNAL_ARGS_BEGIN(StudioStarted, "Studio started")
1290 CDBUS_SIGNAL_ARGS_END
1292 CDBUS_SIGNAL_ARGS_BEGIN(StudioCrashed, "Studio crashed")
1293 CDBUS_SIGNAL_ARGS_END
1295 CDBUS_SIGNAL_ARGS_BEGIN(StudioStopped, "Studio stopped")
1296 CDBUS_SIGNAL_ARGS_END
1298 CDBUS_SIGNAL_ARGS_BEGIN(RoomAppeared, "Room D-Bus object appeared")
1299 CDBUS_SIGNAL_ARG_DESCRIBE("opath", "s", "room object path")
1300 CDBUS_SIGNAL_ARG_DESCRIBE("properties", "a{sv}", "room properties")
1301 CDBUS_SIGNAL_ARGS_END
1303 CDBUS_SIGNAL_ARGS_BEGIN(RoomChanged, "Room D-Bus object changed")
1304 CDBUS_SIGNAL_ARG_DESCRIBE("opath", "s", "room object path")
1305 CDBUS_SIGNAL_ARG_DESCRIBE("properties", "a{sv}", "room properties")
1306 CDBUS_SIGNAL_ARGS_END
1308 CDBUS_SIGNAL_ARGS_BEGIN(RoomDisappeared, "Room D-Bus object disappeared")
1309 CDBUS_SIGNAL_ARG_DESCRIBE("opath", "s", "room object path")
1310 CDBUS_SIGNAL_ARG_DESCRIBE("properties", "a{sv}", "room properties")
1311 CDBUS_SIGNAL_ARGS_END
1313 CDBUS_SIGNALS_BEGIN
1314 CDBUS_SIGNAL_DESCRIBE(StudioRenamed)
1315 CDBUS_SIGNAL_DESCRIBE(StudioStarted)
1316 CDBUS_SIGNAL_DESCRIBE(StudioCrashed)
1317 CDBUS_SIGNAL_DESCRIBE(StudioStopped)
1318 CDBUS_SIGNAL_DESCRIBE(RoomAppeared)
1319 CDBUS_SIGNAL_DESCRIBE(RoomDisappeared)
1320 CDBUS_SIGNAL_DESCRIBE(RoomChanged)
1321 CDBUS_SIGNALS_END
1323 CDBUS_INTERFACE_DEFAULT_HANDLER_METHODS_AND_SIGNALS(g_interface_studio, IFACE_STUDIO)