move catdup to common/
[ladish.git] / daemon / studio.c
blobc31efb0e04bf257387812834e2b947ef2399f5bb
1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
2 /*
3 * LADI Session Handler (ladish)
5 * Copyright (C) 2009, 2010 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>
33 #include <dirent.h>
35 #include "studio_internal.h"
36 #include "../dbus_constants.h"
37 #include "control.h"
38 #include "../common/catdup.h"
39 #include "../common/dirhelpers.h"
40 #include "graph_dict.h"
41 #include "escape.h"
42 #include "studio.h"
43 #include "../proxies/notify_proxy.h"
45 #define STUDIOS_DIR "/studios/"
46 char * g_studios_dir;
48 struct studio g_studio;
50 bool ladish_studio_name_generate(char ** name_ptr)
52 time_t now;
53 char timestamp_str[26];
54 char * name;
56 time(&now);
57 //ctime_r(&now, timestamp_str);
58 //timestamp_str[24] = 0;
59 snprintf(timestamp_str, sizeof(timestamp_str), "%llu", (unsigned long long)now);
61 name = catdup("Studio ", timestamp_str);
62 if (name == NULL)
64 log_error("catdup failed to create studio name");
65 return false;
68 *name_ptr = name;
69 return true;
72 bool ladish_studio_show(void)
74 dbus_object_path object;
76 ASSERT(g_studio.name != NULL);
77 ASSERT(!g_studio.announced);
79 object = dbus_object_path_new(
80 STUDIO_OBJECT_PATH,
81 &g_interface_studio, &g_studio,
82 &g_interface_patchbay, ladish_graph_get_dbus_context(g_studio.studio_graph),
83 &g_iface_graph_dict, g_studio.studio_graph,
84 &g_iface_app_supervisor, g_studio.app_supervisor,
85 NULL);
86 if (object == NULL)
88 log_error("dbus_object_path_new() failed");
89 return false;
92 if (!dbus_object_path_register(g_dbus_connection, object))
94 log_error("object_path_register() failed");
95 dbus_object_path_destroy(g_dbus_connection, object);
96 return false;
99 log_info("Studio D-Bus object created. \"%s\"", g_studio.name);
101 g_studio.dbus_object = object;
103 emit_studio_appeared();
105 return true;
108 void ladish_studio_announce(void)
110 ASSERT(!g_studio.announced);
112 /* notify the user that studio started successfully, but dont lie when jack was started externally */
113 if (!g_studio.automatic)
115 ladish_notify_simple(LADISH_NOTIFY_URGENCY_NORMAL, "Studio loaded", NULL);
118 g_studio.announced = true;
121 bool ladish_studio_publish(void)
123 if (!ladish_studio_show())
125 return false;
128 ladish_studio_announce();
129 return true;
132 void ladish_studio_clear(void)
134 ladish_graph_dump(g_studio.studio_graph);
135 ladish_graph_dump(g_studio.jack_graph);
137 /* remove rooms that own clients in studio graph before clearing it */
138 ladish_studio_remove_all_rooms();
140 ladish_graph_clear(g_studio.studio_graph, NULL);
141 ladish_graph_clear(g_studio.jack_graph, NULL);
143 ladish_studio_jack_conf_clear();
145 g_studio.modified = false;
146 g_studio.persisted = false;
148 ladish_app_supervisor_clear(g_studio.app_supervisor);
150 if (g_studio.dbus_object != NULL)
152 dbus_object_path_destroy(g_dbus_connection, g_studio.dbus_object);
153 g_studio.dbus_object = NULL;
154 emit_studio_disappeared();
157 if (g_studio.announced)
159 ladish_notify_simple(LADISH_NOTIFY_URGENCY_NORMAL, "Studio unloaded", NULL);
160 g_studio.announced = false;
163 if (g_studio.name != NULL)
165 free(g_studio.name);
166 g_studio.name = NULL;
169 if (g_studio.filename != NULL)
171 free(g_studio.filename);
172 g_studio.filename = NULL;
176 void ladish_studio_emit_started(void)
178 dbus_signal_emit(g_dbus_connection, STUDIO_OBJECT_PATH, IFACE_STUDIO, "StudioStarted", "");
181 void ladish_studio_emit_crashed(void)
183 dbus_signal_emit(g_dbus_connection, STUDIO_OBJECT_PATH, IFACE_STUDIO, "StudioCrashed", "");
186 void ladish_studio_emit_stopped(void)
188 dbus_signal_emit(g_dbus_connection, STUDIO_OBJECT_PATH, IFACE_STUDIO, "StudioStopped", "");
191 static bool ladish_studio_fill_room_info(DBusMessageIter * iter_ptr, ladish_room_handle room)
193 DBusMessageIter dict_iter;
194 const char * name;
195 uuid_t template_uuid;
196 ladish_room_handle template;
197 const char * template_name;
198 const char * opath;
200 name = ladish_room_get_name(room);
201 opath = ladish_room_get_opath(room);
203 if (!ladish_room_get_template_uuid(room, template_uuid))
205 template = NULL;
206 template_name = NULL;
208 else
210 template = find_room_template_by_uuid(template_uuid);
211 if (template != NULL)
213 template_name = ladish_room_get_name(template);
215 else
217 template_name = NULL;
221 if (!dbus_message_iter_append_basic(iter_ptr, DBUS_TYPE_STRING, &opath))
223 log_error("dbus_message_iter_append_basic() failed.");
224 return false;
227 if (!dbus_message_iter_open_container(iter_ptr, DBUS_TYPE_ARRAY, "{sv}", &dict_iter))
229 log_error("dbus_message_iter_open_container() failed.");
230 return false;
233 if (!dbus_maybe_add_dict_entry_string(&dict_iter, "template", template_name))
235 log_error("dbus_maybe_add_dict_entry_string() failed.");
236 return false;
239 if (!dbus_maybe_add_dict_entry_string(&dict_iter, "name", name))
241 log_error("dbus_maybe_add_dict_entry_string() failed.");
242 return false;
245 if (!dbus_message_iter_close_container(iter_ptr, &dict_iter))
247 log_error("dbus_message_iter_close_container() failed.");
248 return false;
251 return true;
254 static void ladish_studio_emit_room_appeared(ladish_room_handle room)
256 DBusMessage * message_ptr;
257 DBusMessageIter iter;
259 message_ptr = dbus_message_new_signal(STUDIO_OBJECT_PATH, IFACE_STUDIO, "RoomAppeared");
260 if (message_ptr == NULL)
262 log_error("dbus_message_new_signal() failed.");
263 return;
266 dbus_message_iter_init_append(message_ptr, &iter);
268 if (ladish_studio_fill_room_info(&iter, room))
270 dbus_signal_send(g_dbus_connection, message_ptr);
273 dbus_message_unref(message_ptr);
276 void ladish_studio_emit_room_disappeared(ladish_room_handle room)
278 DBusMessage * message_ptr;
279 DBusMessageIter iter;
281 message_ptr = dbus_message_new_signal(STUDIO_OBJECT_PATH, IFACE_STUDIO, "RoomDisappeared");
282 if (message_ptr == NULL)
284 log_error("dbus_message_new_signal() failed.");
285 return;
288 dbus_message_iter_init_append(message_ptr, &iter);
290 if (ladish_studio_fill_room_info(&iter, room))
292 dbus_signal_send(g_dbus_connection, message_ptr);
295 dbus_message_unref(message_ptr);
298 void ladish_studio_room_appeared(ladish_room_handle room)
300 log_info("Room \"%s\" appeared", ladish_room_get_name(room));
301 list_add_tail(ladish_room_get_list_node(room), &g_studio.rooms);
302 ladish_studio_emit_room_appeared(room);
305 void ladish_studio_room_disappeared(ladish_room_handle room)
307 log_info("Room \"%s\" disappeared", ladish_room_get_name(room));
308 list_del(ladish_room_get_list_node(room));
309 ladish_studio_emit_room_disappeared(room);
312 bool
313 ladish_studio_set_graph_connection_handlers(
314 void * context,
315 ladish_graph_handle graph,
316 ladish_app_supervisor_handle app_supervisor)
318 ladish_virtualizer_set_graph_connection_handlers(context, graph);
319 return true; /* iterate all graphs */
322 void ladish_studio_on_event_jack_started(void)
324 if (!ladish_studio_fetch_jack_settings())
326 log_error("studio_fetch_jack_settings() failed.");
328 return;
331 log_info("jack conf successfully retrieved");
332 g_studio.jack_conf_valid = true;
334 if (!graph_proxy_create(JACKDBUS_SERVICE_NAME, JACKDBUS_OBJECT_PATH, false, &g_studio.jack_graph_proxy))
336 log_error("graph_proxy_create() failed for jackdbus");
338 else
340 if (!ladish_virtualizer_create(g_studio.jack_graph_proxy, g_studio.jack_graph, &g_studio.virtualizer))
342 log_error("ladish_virtualizer_create() failed.");
344 else
346 ladish_studio_iterate_virtual_graphs(g_studio.virtualizer, ladish_studio_set_graph_connection_handlers);
349 if (!graph_proxy_activate(g_studio.jack_graph_proxy))
351 log_error("graph_proxy_activate() failed.");
355 ladish_app_supervisor_autorun(g_studio.app_supervisor);
357 ladish_studio_emit_started();
359 /* notify the user that studio started successfully, but dont lie when jack was started externally */
360 if (!g_studio.automatic)
362 ladish_notify_simple(LADISH_NOTIFY_URGENCY_NORMAL, "Studio started", NULL);
366 static void ladish_studio_on_jack_stopped_internal(void)
368 if (g_studio.virtualizer)
370 ladish_virtualizer_destroy(g_studio.virtualizer);
371 g_studio.virtualizer = NULL;
374 if (g_studio.jack_graph_proxy)
376 graph_proxy_destroy(g_studio.jack_graph_proxy);
377 g_studio.jack_graph_proxy = NULL;
381 void ladish_studio_on_event_jack_stopped(void)
383 ladish_studio_emit_stopped();
384 ladish_studio_on_jack_stopped_internal();
385 ladish_notify_simple(LADISH_NOTIFY_URGENCY_NORMAL, "Studio stopped", NULL);
388 void ladish_studio_handle_unexpected_jack_server_stop(void)
390 ladish_studio_emit_crashed();
391 ladish_studio_on_jack_stopped_internal();
393 /* TODO: if user wants, restart jack server and reconnect all jack apps to it */
396 static bool ladish_studio_hide_vgraph_non_virtual(void * context, ladish_graph_handle graph, ladish_app_supervisor_handle app_supervisor)
398 ladish_graph_hide_non_virtual(graph);
399 return true; /* iterate all vgraphs */
402 void ladish_studio_run(void)
404 bool state;
406 ladish_cqueue_run(&g_studio.cmd_queue);
407 if (g_quit)
408 { /* if quit is requested, don't bother to process external events */
409 return;
412 if (ladish_environment_consume_change(&g_studio.env_store, ladish_environment_jack_server_started, &state))
414 ladish_cqueue_clear(&g_studio.cmd_queue);
416 if (state)
418 ladish_environment_ignore(&g_studio.env_store, ladish_environment_jack_server_present);
420 if (g_studio.jack_graph_proxy != NULL)
422 log_error("Ignoring \"JACK started\" notification because it is already known that JACK is currently started.");
423 return;
426 /* Automatic studio creation on JACK server start */
427 if (g_studio.dbus_object == NULL)
429 ASSERT(g_studio.name == NULL);
430 if (!ladish_studio_name_generate(&g_studio.name))
432 log_error("studio_name_generate() failed.");
433 return;
436 g_studio.automatic = true;
438 ladish_studio_publish();
441 ladish_studio_on_event_jack_started();
443 if (g_studio.automatic)
445 ladish_notify_simple(
446 LADISH_NOTIFY_URGENCY_NORMAL,
447 "Automatic studio created",
448 "JACK server is started not by ladish daemon and there is no loaded studio, so a new studio is created and marked as started.");
451 else
453 /* JACK stopped but this was not expected. When expected.
454 * the change will be consumed by the run method of the studio stop command */
456 if (g_studio.jack_graph_proxy == NULL)
458 log_error("Ignoring \"JACK stopped\" notification because it is already known that JACK is currently stopped.");
459 return;
462 if (g_studio.automatic)
464 log_info("Unloading automatic studio.");
465 ladish_command_unload_studio(NULL, &g_studio.cmd_queue);
467 ladish_studio_on_event_jack_stopped();
468 return;
471 log_error("JACK stopped unexpectedly.");
472 log_error("Save your work, then unload and reload the studio.");
473 ladish_notify_simple(
474 LADISH_NOTIFY_URGENCY_HIGH,
475 "Studio crashed",
476 "JACK stopped unexpectedly.\n\n"
477 "Save your work, then unload and reload the studio.");
478 ladish_studio_handle_unexpected_jack_server_stop();
482 if (ladish_environment_consume_change(&g_studio.env_store, ladish_environment_jack_server_present, &state))
484 if (g_studio.jack_graph_proxy != NULL)
486 ladish_cqueue_clear(&g_studio.cmd_queue);
488 /* jack was started, this probably means that jackdbus has crashed */
489 log_error("JACK disappeared unexpectedly. Maybe it crashed.");
490 log_error("Save your work, then unload and reload the studio.");
491 ladish_notify_simple(
492 LADISH_NOTIFY_URGENCY_HIGH,
493 "Studio crashed",
494 "JACK disappeared unexpectedly. Maybe it crashed.\n\n"
495 "Save your work, then unload and reload the studio.");
496 ladish_environment_reset_stealth(&g_studio.env_store, ladish_environment_jack_server_started);
498 ladish_studio_iterate_virtual_graphs(NULL, ladish_studio_hide_vgraph_non_virtual);
500 ladish_studio_handle_unexpected_jack_server_stop();
504 ladish_environment_assert_consumed(&g_studio.env_store);
507 static void ladish_studio_on_jack_server_started(void)
509 log_info("JACK server start detected.");
510 ladish_environment_set(&g_studio.env_store, ladish_environment_jack_server_started);
513 static void ladish_studio_on_jack_server_stopped(void)
515 log_info("JACK server stop detected.");
516 ladish_environment_reset(&g_studio.env_store, ladish_environment_jack_server_started);
519 static void ladish_studio_on_jack_server_appeared(void)
521 log_info("JACK controller appeared.");
522 ladish_environment_set(&g_studio.env_store, ladish_environment_jack_server_present);
525 static void ladish_studio_on_jack_server_disappeared(void)
527 log_info("JACK controller disappeared.");
528 ladish_environment_reset(&g_studio.env_store, ladish_environment_jack_server_present);
531 bool ladish_studio_init(void)
533 log_info("studio object construct");
535 g_studios_dir = catdup(g_base_dir, STUDIOS_DIR);
536 if (g_studios_dir == NULL)
538 log_error("catdup failed for '%s' and '%s'", g_base_dir, STUDIOS_DIR);
539 goto fail;
542 if (!ensure_dir_exist(g_studios_dir, 0700))
544 goto free_studios_dir;
547 INIT_LIST_HEAD(&g_studio.all_connections);
548 INIT_LIST_HEAD(&g_studio.all_ports);
549 INIT_LIST_HEAD(&g_studio.all_clients);
550 INIT_LIST_HEAD(&g_studio.jack_connections);
551 INIT_LIST_HEAD(&g_studio.jack_ports);
552 INIT_LIST_HEAD(&g_studio.jack_clients);
553 INIT_LIST_HEAD(&g_studio.rooms);
554 INIT_LIST_HEAD(&g_studio.clients);
555 INIT_LIST_HEAD(&g_studio.ports);
557 INIT_LIST_HEAD(&g_studio.jack_conf);
558 INIT_LIST_HEAD(&g_studio.jack_params);
560 g_studio.dbus_object = NULL;
561 g_studio.announced = false;
562 g_studio.name = NULL;
563 g_studio.filename = NULL;
565 g_studio.room_count = 0;
567 if (!ladish_graph_create(&g_studio.jack_graph, NULL))
569 log_error("ladish_graph_create() failed to create jack graph object.");
570 goto free_studios_dir;
573 if (!ladish_graph_create(&g_studio.studio_graph, STUDIO_OBJECT_PATH))
575 log_error("ladish_graph_create() failed to create studio graph object.");
576 goto jack_graph_destroy;
579 if (!ladish_app_supervisor_create(&g_studio.app_supervisor, STUDIO_OBJECT_PATH, "studio", g_studio.studio_graph, ladish_virtualizer_rename_app))
581 log_error("ladish_app_supervisor_create() failed.");
582 goto studio_graph_destroy;
585 ladish_cqueue_init(&g_studio.cmd_queue);
586 ladish_environment_init(&g_studio.env_store);
588 if (!jack_proxy_init(
589 ladish_studio_on_jack_server_started,
590 ladish_studio_on_jack_server_stopped,
591 ladish_studio_on_jack_server_appeared,
592 ladish_studio_on_jack_server_disappeared))
594 log_error("jack_proxy_init() failed.");
595 goto app_supervisor_destroy;
598 return true;
600 app_supervisor_destroy:
601 ladish_app_supervisor_destroy(g_studio.app_supervisor);
602 studio_graph_destroy:
603 ladish_graph_destroy(g_studio.studio_graph);
604 jack_graph_destroy:
605 ladish_graph_destroy(g_studio.jack_graph);
606 free_studios_dir:
607 free(g_studios_dir);
608 fail:
609 return false;
612 void ladish_studio_uninit(void)
614 log_info("studio_uninit()");
616 jack_proxy_uninit();
618 ladish_cqueue_clear(&g_studio.cmd_queue);
620 ladish_graph_destroy(g_studio.studio_graph);
621 ladish_graph_destroy(g_studio.jack_graph);
623 free(g_studios_dir);
625 log_info("studio object destroy");
628 struct on_child_exit_context
630 pid_t pid;
631 bool found;
634 #define child_exit_context_ptr ((struct on_child_exit_context *)context)
636 static
637 bool
638 ladish_studio_on_child_exit_callback(
639 void * context,
640 ladish_graph_handle graph,
641 ladish_app_supervisor_handle app_supervisor)
643 child_exit_context_ptr->found = ladish_app_supervisor_child_exit(app_supervisor, child_exit_context_ptr->pid);
644 /* if child is found, return false - it will cause iteration to stop */
645 /* if child is not found, return true - it will cause next supervisor to be checked */
646 return !child_exit_context_ptr->found;
649 #undef child_exit_context_ptr
651 void ladish_studio_on_child_exit(pid_t pid)
653 struct on_child_exit_context context;
655 context.pid = pid;
656 context.found = false;
658 ladish_studio_iterate_virtual_graphs(&context, ladish_studio_on_child_exit_callback);
660 if (!context.found)
662 log_error("unknown child exit detected. pid is %llu", (unsigned long long)pid);
666 bool ladish_studio_is_loaded(void)
668 return g_studio.dbus_object != NULL && g_studio.announced;
671 bool ladish_studio_is_started(void)
673 return ladish_environment_get(&g_studio.env_store, ladish_environment_jack_server_started);
676 bool ladish_studio_compose_filename(const char * name, char ** filename_ptr_ptr, char ** backup_filename_ptr_ptr)
678 size_t len_dir;
679 char * p;
680 const char * src;
681 char * filename_ptr;
682 char * backup_filename_ptr = NULL;
684 len_dir = strlen(g_studios_dir);
686 filename_ptr = malloc(len_dir + 1 + strlen(name) * 3 + 4 + 1);
687 if (filename_ptr == NULL)
689 log_error("malloc failed to allocate memory for studio file path");
690 return false;
693 if (backup_filename_ptr_ptr != NULL)
695 backup_filename_ptr = malloc(len_dir + 1 + strlen(name) * 3 + 4 + 4 + 1);
696 if (backup_filename_ptr == NULL)
698 log_error("malloc failed to allocate memory for studio backup file path");
699 free(filename_ptr);
700 return false;
704 p = filename_ptr;
705 memcpy(p, g_studios_dir, len_dir);
706 p += len_dir;
708 *p++ = '/';
710 src = name;
711 escape(&src, &p);
712 strcpy(p, ".xml");
714 *filename_ptr_ptr = filename_ptr;
716 if (backup_filename_ptr_ptr != NULL)
718 strcpy(backup_filename_ptr, filename_ptr);
719 strcat(backup_filename_ptr, ".bak");
720 *backup_filename_ptr_ptr = backup_filename_ptr;
723 return true;
726 struct ladish_cqueue * ladish_studio_get_cmd_queue(void)
728 return &g_studio.cmd_queue;
731 ladish_virtualizer_handle ladish_studio_get_virtualizer(void)
733 return g_studio.virtualizer;
736 ladish_graph_handle ladish_studio_get_jack_graph(void)
738 return g_studio.jack_graph;
741 ladish_graph_handle ladish_studio_get_studio_graph(void)
743 return g_studio.studio_graph;
746 ladish_app_supervisor_handle ladish_studio_get_studio_app_supervisor(void)
748 return g_studio.app_supervisor;
751 struct ladish_studio_app_supervisor_match_context
753 const char * opath;
754 ladish_app_supervisor_handle supervisor;
757 #define iterate_context_ptr ((struct ladish_studio_app_supervisor_match_context *)context)
759 static bool ladish_studio_app_supervisor_match(void * context, ladish_graph_handle graph, ladish_app_supervisor_handle app_supervisor)
761 ASSERT(strcmp(ladish_app_supervisor_get_opath(app_supervisor), ladish_graph_get_opath(graph)) == 0);
762 if (strcmp(ladish_app_supervisor_get_opath(app_supervisor), iterate_context_ptr->opath) == 0)
764 ASSERT(iterate_context_ptr->supervisor == NULL);
765 iterate_context_ptr->supervisor = app_supervisor;
766 return false; /* stop iteration */
769 return true; /* continue iteration */
772 #undef iterate_context_ptr
774 ladish_app_supervisor_handle ladish_studio_find_app_supervisor(const char * opath)
776 struct ladish_studio_app_supervisor_match_context ctx;
778 ctx.opath = opath;
779 ctx.supervisor = NULL;
780 ladish_studio_iterate_virtual_graphs(&ctx, ladish_studio_app_supervisor_match);
781 return ctx.supervisor;
784 bool ladish_studios_iterate(void * call_ptr, void * context, bool (* callback)(void * call_ptr, void * context, const char * studio, uint32_t modtime))
786 DIR * dir;
787 struct dirent * dentry;
788 size_t len;
789 struct stat st;
790 char * path;
791 char * name;
793 dir = opendir(g_studios_dir);
794 if (dir == NULL)
796 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Cannot open directory '%s': %d (%s)", g_studios_dir, errno, strerror(errno));
797 return false;
800 while ((dentry = readdir(dir)) != NULL)
802 len = strlen(dentry->d_name);
803 if (len <= 4 || strcmp(dentry->d_name + (len - 4), ".xml") != 0)
804 continue;
806 path = catdup(g_studios_dir, dentry->d_name);
807 if (path == NULL)
809 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "catdup() failed");
810 return false;
813 if (stat(path, &st) != 0)
815 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "failed to stat '%s': %d (%s)", path, errno, strerror(errno));
816 free(path);
817 return false;
820 free(path);
822 if (!S_ISREG(st.st_mode))
824 //log_info("Ignoring direntry that is not regular file. Mode is %07o", st.st_mode);
825 continue;
828 name = malloc(len - 4 + 1);
829 if (name == NULL)
831 log_error("malloc() failed.");
832 closedir(dir);
833 return false;
836 name[unescape(dentry->d_name, len - 4, name)] = 0;
837 //log_info("name = '%s'", name);
839 if (!callback(call_ptr, context, name, st.st_mtime))
841 free(name);
842 closedir(dir);
843 return false;
846 free(name);
849 closedir(dir);
850 return true;
853 bool ladish_studio_delete(void * call_ptr, const char * studio_name)
855 char * filename;
856 char * bak_filename;
857 struct stat st;
858 bool ret;
860 ret = false;
862 if (!ladish_studio_compose_filename(studio_name, &filename, &bak_filename))
864 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "failed to compose studio filename");
865 goto exit;
868 log_info("Deleting studio ('%s')", filename);
870 if (unlink(filename) != 0)
872 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "unlink(%s) failed: %d (%s)", filename, errno, strerror(errno));
873 goto free;
876 /* try to delete the backup file */
877 if (stat(bak_filename, &st) == 0)
879 if (unlink(bak_filename) != 0)
881 /* failing to delete backup file will not case delete command failure */
882 log_error("unlink(%s) failed: %d (%s)", bak_filename, errno, strerror(errno));
886 ret = true;
888 free:
889 free(filename);
890 free(bak_filename);
891 exit:
892 return ret;
895 bool
896 ladish_studio_iterate_virtual_graphs(
897 void * context,
898 bool (* callback)(
899 void * context,
900 ladish_graph_handle graph,
901 ladish_app_supervisor_handle app_supervisor))
903 struct list_head * node_ptr;
904 ladish_room_handle room;
905 ladish_app_supervisor_handle room_app_supervisor;
906 ladish_graph_handle room_graph;
908 if (!callback(context, g_studio.studio_graph, g_studio.app_supervisor))
910 return false;
913 list_for_each(node_ptr, &g_studio.rooms)
915 room = ladish_room_from_list_node(node_ptr);
916 room_app_supervisor = ladish_room_get_app_supervisor(room);
917 ASSERT(room_app_supervisor != NULL);
918 room_graph = ladish_room_get_graph(room);
920 if (!callback(context, room_graph, room_app_supervisor))
922 return false;
926 return true;
929 bool
930 ladish_studio_iterate_rooms(
931 void * context,
932 bool (* callback)(
933 void * context,
934 ladish_room_handle room))
936 struct list_head * node_ptr;
937 ladish_room_handle room;
939 list_for_each(node_ptr, &g_studio.rooms)
941 room = ladish_room_from_list_node(node_ptr);
942 if (!callback(context, room))
944 return false;
948 return true;
951 bool ladish_studio_has_rooms(void)
953 return !list_empty(&g_studio.rooms);
956 static bool ladish_studio_stop_app_supervisor(void * context, ladish_graph_handle graph, ladish_app_supervisor_handle app_supervisor)
958 ladish_app_supervisor_stop(app_supervisor);
959 return true; /* iterate all supervisors */
962 void ladish_studio_stop_app_supervisors(void)
964 ladish_studio_iterate_virtual_graphs(NULL, ladish_studio_stop_app_supervisor);
967 void ladish_studio_emit_renamed(void)
969 dbus_signal_emit(g_dbus_connection, STUDIO_OBJECT_PATH, IFACE_STUDIO, "StudioRenamed", "s", &g_studio.name);
972 unsigned int ladish_studio_get_room_index(void)
974 return ++g_studio.room_count;
977 void ladish_studio_release_room_index(unsigned int index)
979 if (index == g_studio.room_count)
981 g_studio.room_count--;
985 void ladish_studio_remove_all_rooms(void)
987 while (!list_empty(&g_studio.rooms))
989 ladish_room_destroy(ladish_room_from_list_node(g_studio.rooms.next));
993 ladish_room_handle ladish_studio_find_room_by_uuid(const uuid_t room_uuid_ptr)
995 struct list_head * node_ptr;
996 ladish_room_handle room;
997 uuid_t room_uuid;
999 list_for_each(node_ptr, &g_studio.rooms)
1001 room = ladish_room_from_list_node(node_ptr);
1002 ladish_room_get_uuid(room, room_uuid);
1003 if (uuid_compare(room_uuid, room_uuid_ptr) == 0)
1005 return room;
1009 return NULL;
1012 bool ladish_studio_check_room_name(const char * room_name)
1014 struct list_head * node_ptr;
1015 ladish_room_handle room;
1017 list_for_each(node_ptr, &g_studio.rooms)
1019 room = ladish_room_from_list_node(node_ptr);
1020 if (strcmp(ladish_room_get_name(room), room_name) == 0)
1022 return true;
1026 return false;
1029 /**********************************************************************************/
1030 /* D-Bus methods */
1031 /**********************************************************************************/
1033 static void ladish_studio_dbus_get_name(struct dbus_method_call * call_ptr)
1035 method_return_new_single(call_ptr, DBUS_TYPE_STRING, &g_studio.name);
1038 static void ladish_studio_dbus_rename(struct dbus_method_call * call_ptr)
1040 const char * new_name;
1041 char * new_name_dup;
1043 if (!dbus_message_get_args(call_ptr->message, &g_dbus_error, DBUS_TYPE_STRING, &new_name, DBUS_TYPE_INVALID))
1045 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
1046 dbus_error_free(&g_dbus_error);
1047 return;
1050 log_info("Rename studio request (%s)", new_name);
1052 new_name_dup = strdup(new_name);
1053 if (new_name_dup == NULL)
1055 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "strdup() failed to allocate new name.");
1056 return;
1059 free(g_studio.name);
1060 g_studio.name = new_name_dup;
1062 method_return_new_void(call_ptr);
1063 ladish_studio_emit_renamed();
1066 static void ladish_studio_dbus_save(struct dbus_method_call * call_ptr)
1068 log_info("Save studio request");
1070 if (ladish_command_save_studio(call_ptr, &g_studio.cmd_queue, g_studio.name))
1072 method_return_new_void(call_ptr);
1076 static void ladish_studio_dbus_save_as(struct dbus_method_call * call_ptr)
1078 const char * new_name;
1080 log_info("SaveAs studio request");
1082 if (!dbus_message_get_args(call_ptr->message, &g_dbus_error, DBUS_TYPE_STRING, &new_name, DBUS_TYPE_INVALID))
1084 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
1085 dbus_error_free(&g_dbus_error);
1086 return;
1089 if (ladish_command_save_studio(call_ptr, &g_studio.cmd_queue, new_name))
1091 method_return_new_void(call_ptr);
1095 static void ladish_studio_dbus_unload(struct dbus_method_call * call_ptr)
1097 log_info("Unload studio request");
1099 if (ladish_command_unload_studio(call_ptr, &g_studio.cmd_queue))
1101 method_return_new_void(call_ptr);
1105 static void ladish_studio_dbus_stop(struct dbus_method_call * call_ptr)
1107 log_info("Stop studio request");
1109 g_studio.automatic = false; /* even if it was automatic, it is not anymore because user knows about it */
1111 if (ladish_command_stop_studio(call_ptr, &g_studio.cmd_queue))
1113 method_return_new_void(call_ptr);
1117 static void ladish_studio_dbus_start(struct dbus_method_call * call_ptr)
1119 log_info("Start studio request");
1121 g_studio.automatic = false; /* even if it was automatic, it is not anymore because user knows about it */
1123 if (ladish_command_start_studio(call_ptr, &g_studio.cmd_queue))
1125 method_return_new_void(call_ptr);
1129 static void ladish_studio_dbus_is_started(struct dbus_method_call * call_ptr)
1131 dbus_bool_t started;
1133 started = g_studio.jack_graph_proxy != NULL;
1135 method_return_new_single(call_ptr, DBUS_TYPE_BOOLEAN, &started);
1138 static void ladish_studio_dbus_create_room(struct dbus_method_call * call_ptr)
1140 const char * room_name;
1141 const char * template_name;
1143 dbus_error_init(&g_dbus_error);
1145 if (!dbus_message_get_args(call_ptr->message, &g_dbus_error, DBUS_TYPE_STRING, &room_name, DBUS_TYPE_STRING, &template_name, DBUS_TYPE_INVALID))
1147 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
1148 dbus_error_free(&g_dbus_error);
1149 return;
1152 if (ladish_command_create_room(call_ptr, ladish_studio_get_cmd_queue(), room_name, template_name))
1154 method_return_new_void(call_ptr);
1158 static void ladish_studio_dbus_get_room_list(struct dbus_method_call * call_ptr)
1160 DBusMessageIter iter, array_iter;
1161 DBusMessageIter struct_iter;
1162 struct list_head * node_ptr;
1163 ladish_room_handle room;
1165 call_ptr->reply = dbus_message_new_method_return(call_ptr->message);
1166 if (call_ptr->reply == NULL)
1168 goto fail;
1171 dbus_message_iter_init_append(call_ptr->reply, &iter);
1173 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sa{sv})", &array_iter))
1175 goto fail_unref;
1178 list_for_each(node_ptr, &g_studio.rooms)
1180 room = ladish_room_from_list_node(node_ptr);
1182 if (!dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter))
1183 goto fail_unref;
1185 if (!ladish_studio_fill_room_info(&struct_iter, room))
1186 goto fail_unref;
1188 if (!dbus_message_iter_close_container(&array_iter, &struct_iter))
1189 goto fail_unref;
1192 if (!dbus_message_iter_close_container(&iter, &array_iter))
1194 goto fail_unref;
1197 return;
1199 fail_unref:
1200 dbus_message_unref(call_ptr->reply);
1201 call_ptr->reply = NULL;
1203 fail:
1204 log_error("Ran out of memory trying to construct method return");
1207 static void ladish_studio_dbus_delete_room(struct dbus_method_call * call_ptr)
1209 const char * name;
1211 dbus_error_init(&g_dbus_error);
1213 if (!dbus_message_get_args(call_ptr->message, &g_dbus_error, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID))
1215 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
1216 dbus_error_free(&g_dbus_error);
1217 return;
1220 if (ladish_command_delete_room(call_ptr, ladish_studio_get_cmd_queue(), name))
1222 method_return_new_void(call_ptr);
1226 METHOD_ARGS_BEGIN(GetName, "Get studio name")
1227 METHOD_ARG_DESCRIBE_OUT("studio_name", "s", "Name of studio")
1228 METHOD_ARGS_END
1230 METHOD_ARGS_BEGIN(Rename, "Rename studio")
1231 METHOD_ARG_DESCRIBE_IN("studio_name", "s", "New name")
1232 METHOD_ARGS_END
1234 METHOD_ARGS_BEGIN(Save, "Save studio")
1235 METHOD_ARGS_END
1237 METHOD_ARGS_BEGIN(SaveAs, "SaveAs studio")
1238 METHOD_ARG_DESCRIBE_IN("studio_name", "s", "New name")
1239 METHOD_ARGS_END
1241 METHOD_ARGS_BEGIN(Unload, "Unload studio")
1242 METHOD_ARGS_END
1244 METHOD_ARGS_BEGIN(Start, "Start studio")
1245 METHOD_ARGS_END
1247 METHOD_ARGS_BEGIN(Stop, "Stop studio")
1248 METHOD_ARGS_END
1250 METHOD_ARGS_BEGIN(IsStarted, "Check whether studio is started")
1251 METHOD_ARG_DESCRIBE_OUT("started", "b", "Whether studio is started")
1252 METHOD_ARGS_END
1254 METHOD_ARGS_BEGIN(CreateRoom, "Create new studio room")
1255 METHOD_ARG_DESCRIBE_IN("room_name", "s", "Studio room name")
1256 METHOD_ARG_DESCRIBE_IN("room_template_name", "s", "Room template name")
1257 METHOD_ARGS_END
1259 METHOD_ARGS_BEGIN(GetRoomList, "Get list of rooms in this studio")
1260 METHOD_ARG_DESCRIBE_OUT("room_list", "a(sa{sv})", "List of studio rooms: opaths and properties")
1261 METHOD_ARGS_END
1263 METHOD_ARGS_BEGIN(DeleteRoom, "Delete studio room")
1264 METHOD_ARG_DESCRIBE_IN("room_name", "s", "Name of studio room to delete")
1265 METHOD_ARGS_END
1267 METHODS_BEGIN
1268 METHOD_DESCRIBE(GetName, ladish_studio_dbus_get_name) /* sync */
1269 METHOD_DESCRIBE(Rename, ladish_studio_dbus_rename) /* sync */
1270 METHOD_DESCRIBE(Save, ladish_studio_dbus_save) /* async */
1271 METHOD_DESCRIBE(SaveAs, ladish_studio_dbus_save_as) /* async */
1272 METHOD_DESCRIBE(Unload, ladish_studio_dbus_unload) /* async */
1273 METHOD_DESCRIBE(Start, ladish_studio_dbus_start) /* async */
1274 METHOD_DESCRIBE(Stop, ladish_studio_dbus_stop) /* async */
1275 METHOD_DESCRIBE(IsStarted, ladish_studio_dbus_is_started) /* sync */
1276 METHOD_DESCRIBE(CreateRoom, ladish_studio_dbus_create_room) /* async */
1277 METHOD_DESCRIBE(GetRoomList, ladish_studio_dbus_get_room_list) /* sync */
1278 METHOD_DESCRIBE(DeleteRoom, ladish_studio_dbus_delete_room) /* async */
1279 METHODS_END
1281 SIGNAL_ARGS_BEGIN(StudioRenamed, "Studio name changed")
1282 SIGNAL_ARG_DESCRIBE("studio_name", "s", "New studio name")
1283 SIGNAL_ARGS_END
1285 SIGNAL_ARGS_BEGIN(StudioStarted, "Studio started")
1286 SIGNAL_ARGS_END
1288 SIGNAL_ARGS_BEGIN(StudioCrashed, "Studio crashed")
1289 SIGNAL_ARGS_END
1291 SIGNAL_ARGS_BEGIN(StudioStopped, "Studio stopped")
1292 SIGNAL_ARGS_END
1294 SIGNAL_ARGS_BEGIN(RoomAppeared, "Room D-Bus object appeared")
1295 SIGNAL_ARG_DESCRIBE("opath", "s", "room object path")
1296 SIGNAL_ARG_DESCRIBE("properties", "a{sv}", "room properties")
1297 SIGNAL_ARGS_END
1299 SIGNAL_ARGS_BEGIN(RoomChanged, "Room D-Bus object changed")
1300 SIGNAL_ARG_DESCRIBE("opath", "s", "room object path")
1301 SIGNAL_ARG_DESCRIBE("properties", "a{sv}", "room properties")
1302 SIGNAL_ARGS_END
1304 SIGNAL_ARGS_BEGIN(RoomDisappeared, "Room D-Bus object disappeared")
1305 SIGNAL_ARG_DESCRIBE("opath", "s", "room object path")
1306 SIGNAL_ARG_DESCRIBE("properties", "a{sv}", "room properties")
1307 SIGNAL_ARGS_END
1309 SIGNALS_BEGIN
1310 SIGNAL_DESCRIBE(StudioRenamed)
1311 SIGNAL_DESCRIBE(StudioStarted)
1312 SIGNAL_DESCRIBE(StudioCrashed)
1313 SIGNAL_DESCRIBE(StudioStopped)
1314 SIGNAL_DESCRIBE(RoomAppeared)
1315 SIGNAL_DESCRIBE(RoomDisappeared)
1316 SIGNAL_DESCRIBE(RoomChanged)
1317 SIGNALS_END
1319 INTERFACE_BEGIN(g_interface_studio, IFACE_STUDIO)
1320 INTERFACE_DEFAULT_HANDLER
1321 INTERFACE_EXPOSE_METHODS
1322 INTERFACE_EXPOSE_SIGNALS
1323 INTERFACE_END