update laditools submodule
[ladish.git] / daemon / studio.c
blob842c8103cd858a1288020b9467e833bc34009b41
1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
2 /*
3 * LADI Session Handler (ladish)
5 * Copyright (C) 2009 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 "../catdup.h"
39 #include "dirhelpers.h"
40 #include "graph_dict.h"
41 #include "escape.h"
43 #define STUDIOS_DIR "/studios/"
44 char * g_studios_dir;
46 struct studio g_studio;
48 bool studio_name_generate(char ** name_ptr)
50 time_t now;
51 char timestamp_str[26];
52 char * name;
54 time(&now);
55 //ctime_r(&now, timestamp_str);
56 //timestamp_str[24] = 0;
57 snprintf(timestamp_str, sizeof(timestamp_str), "%llu", (unsigned long long)now);
59 name = catdup("Studio ", timestamp_str);
60 if (name == NULL)
62 log_error("catdup failed to create studio name");
63 return false;
66 *name_ptr = name;
67 return true;
70 bool
71 studio_publish(void)
73 dbus_object_path object;
75 ASSERT(g_studio.name != NULL);
77 object = dbus_object_path_new(
78 STUDIO_OBJECT_PATH,
79 &g_interface_studio, &g_studio,
80 &g_interface_patchbay, ladish_graph_get_dbus_context(g_studio.studio_graph),
81 &g_iface_graph_dict, g_studio.studio_graph,
82 &g_iface_app_supervisor, g_studio.app_supervisor,
83 NULL);
84 if (object == NULL)
86 log_error("dbus_object_path_new() failed");
87 return false;
90 if (!dbus_object_path_register(g_dbus_connection, object))
92 log_error("object_path_register() failed");
93 dbus_object_path_destroy(g_dbus_connection, object);
94 return false;
97 log_info("Studio D-Bus object created. \"%s\"", g_studio.name);
99 g_studio.dbus_object = object;
101 emit_studio_appeared();
103 return true;
106 void emit_studio_started(void)
108 dbus_signal_emit(g_dbus_connection, STUDIO_OBJECT_PATH, IFACE_STUDIO, "StudioStarted", "");
111 void emit_studio_crashed(void)
113 dbus_signal_emit(g_dbus_connection, STUDIO_OBJECT_PATH, IFACE_STUDIO, "StudioCrashed", "");
116 void emit_studio_stopped(void)
118 dbus_signal_emit(g_dbus_connection, STUDIO_OBJECT_PATH, IFACE_STUDIO, "StudioStopped", "");
121 void on_event_jack_started(void)
123 if (!studio_fetch_jack_settings())
125 log_error("studio_fetch_jack_settings() failed.");
127 return;
130 log_info("jack conf successfully retrieved");
131 g_studio.jack_conf_valid = true;
133 if (!graph_proxy_create(JACKDBUS_SERVICE_NAME, JACKDBUS_OBJECT_PATH, false, &g_studio.jack_graph_proxy))
135 log_error("graph_proxy_create() failed for jackdbus");
137 else
139 if (!ladish_virtualizer_create(g_studio.jack_graph_proxy, g_studio.jack_graph, g_studio.studio_graph, &g_studio.virtualizer))
141 log_error("ladish_virtualizer_create() failed.");
144 if (!graph_proxy_activate(g_studio.jack_graph_proxy))
146 log_error("graph_proxy_activate() failed.");
150 ladish_app_supervisor_autorun(g_studio.app_supervisor);
152 emit_studio_started();
155 static void on_jack_stopped_internal(void)
157 if (g_studio.automatic)
159 log_info("Unloading automatic studio.");
160 ladish_command_unload_studio(NULL, &g_studio.cmd_queue);
161 return;
164 if (g_studio.virtualizer)
166 ladish_virtualizer_destroy(g_studio.virtualizer);
167 g_studio.virtualizer = NULL;
170 if (g_studio.jack_graph_proxy)
172 graph_proxy_destroy(g_studio.jack_graph_proxy);
173 g_studio.jack_graph_proxy = NULL;
177 void on_event_jack_stopped(void)
179 emit_studio_stopped();
180 on_jack_stopped_internal();
183 void handle_unexpected_jack_server_stop(void)
185 emit_studio_crashed();
186 on_jack_stopped_internal();
188 /* TODO: if user wants, restart jack server and reconnect all jack apps to it */
191 void studio_run(void)
193 bool state;
195 ladish_cqueue_run(&g_studio.cmd_queue);
196 if (g_quit)
197 { /* if quit is requested, don't bother to process external events */
198 return;
201 if (ladish_environment_consume_change(&g_studio.env_store, ladish_environment_jack_server_started, &state))
203 ladish_cqueue_clear(&g_studio.cmd_queue);
205 if (state)
207 ladish_environment_ignore(&g_studio.env_store, ladish_environment_jack_server_present);
209 /* Automatic studio creation on JACK server start */
210 if (g_studio.dbus_object == NULL)
212 ASSERT(g_studio.name == NULL);
213 if (!studio_name_generate(&g_studio.name))
215 log_error("studio_name_generate() failed.");
216 return;
219 g_studio.automatic = true;
221 studio_publish();
224 on_event_jack_started();
226 else
228 /* JACK stopped but this was not expected. When expected.
229 * the change will be consumed by the run method of the studio stop command */
230 log_error("JACK stopped unexpectedly.");
231 log_error("Save your work, then unload and reload the studio.");
232 handle_unexpected_jack_server_stop();
236 if (ladish_environment_consume_change(&g_studio.env_store, ladish_environment_jack_server_present, &state))
238 if (g_studio.jack_graph_proxy != NULL)
240 ladish_cqueue_clear(&g_studio.cmd_queue);
242 /* jack was started, this probably means that jackdbus has crashed */
243 log_error("JACK disappeared unexpectedly. Maybe it crashed.");
244 log_error("Save your work, then unload and reload the studio.");
245 ladish_environment_reset_stealth(&g_studio.env_store, ladish_environment_jack_server_started);
247 ladish_graph_clear(g_studio.studio_graph, false);
248 ladish_graph_clear(g_studio.jack_graph, true);
250 handle_unexpected_jack_server_stop();
254 ladish_environment_assert_consumed(&g_studio.env_store);
257 static void on_jack_server_started(void)
259 log_info("JACK server start detected.");
260 ladish_environment_set(&g_studio.env_store, ladish_environment_jack_server_started);
263 static void on_jack_server_stopped(void)
265 log_info("JACK server stop detected.");
266 ladish_environment_reset(&g_studio.env_store, ladish_environment_jack_server_started);
269 static void on_jack_server_appeared(void)
271 log_info("JACK controller appeared.");
272 ladish_environment_set(&g_studio.env_store, ladish_environment_jack_server_present);
275 static void on_jack_server_disappeared(void)
277 log_info("JACK controller disappeared.");
278 ladish_environment_reset(&g_studio.env_store, ladish_environment_jack_server_present);
281 bool studio_init(void)
283 log_info("studio object construct");
285 g_studios_dir = catdup(g_base_dir, STUDIOS_DIR);
286 if (g_studios_dir == NULL)
288 log_error("catdup failed for '%s' and '%s'", g_base_dir, STUDIOS_DIR);
289 goto fail;
292 if (!ensure_dir_exist(g_studios_dir, 0700))
294 goto free_studios_dir;
297 INIT_LIST_HEAD(&g_studio.all_connections);
298 INIT_LIST_HEAD(&g_studio.all_ports);
299 INIT_LIST_HEAD(&g_studio.all_clients);
300 INIT_LIST_HEAD(&g_studio.jack_connections);
301 INIT_LIST_HEAD(&g_studio.jack_ports);
302 INIT_LIST_HEAD(&g_studio.jack_clients);
303 INIT_LIST_HEAD(&g_studio.rooms);
304 INIT_LIST_HEAD(&g_studio.clients);
305 INIT_LIST_HEAD(&g_studio.ports);
307 INIT_LIST_HEAD(&g_studio.jack_conf);
308 INIT_LIST_HEAD(&g_studio.jack_params);
310 g_studio.dbus_object = NULL;
311 g_studio.name = NULL;
312 g_studio.filename = NULL;
314 if (!ladish_graph_create(&g_studio.jack_graph, NULL))
316 log_error("ladish_graph_create() failed to create jack graph object.");
317 goto free_studios_dir;
320 if (!ladish_graph_create(&g_studio.studio_graph, STUDIO_OBJECT_PATH))
322 log_error("ladish_graph_create() failed to create studio graph object.");
323 goto jack_graph_destroy;
326 if (!ladish_app_supervisor_create(&g_studio.app_supervisor, STUDIO_OBJECT_PATH, "studio"))
328 log_error("ladish_app_supervisor_create() failed.");
329 goto studio_graph_destroy;
332 ladish_cqueue_init(&g_studio.cmd_queue);
333 ladish_environment_init(&g_studio.env_store);
335 if (!jack_proxy_init(
336 on_jack_server_started,
337 on_jack_server_stopped,
338 on_jack_server_appeared,
339 on_jack_server_disappeared))
341 log_error("jack_proxy_init() failed.");
342 goto app_supervisor_destroy;
345 return true;
347 app_supervisor_destroy:
348 ladish_app_supervisor_destroy(g_studio.app_supervisor);
349 studio_graph_destroy:
350 ladish_graph_destroy(g_studio.studio_graph, false);
351 jack_graph_destroy:
352 ladish_graph_destroy(g_studio.jack_graph, false);
353 free_studios_dir:
354 free(g_studios_dir);
355 fail:
356 return false;
359 void studio_uninit(void)
361 log_info("studio_uninit()");
363 jack_proxy_uninit();
365 ladish_cqueue_clear(&g_studio.cmd_queue);
367 ladish_graph_destroy(g_studio.studio_graph, false);
368 ladish_graph_destroy(g_studio.jack_graph, false);
370 free(g_studios_dir);
372 log_info("studio object destroy");
375 void studio_on_child_exit(pid_t pid)
377 if (!ladish_app_supervisor_child_exit(g_studio.app_supervisor, pid))
379 log_error("non-studio child exit detected. pid is %llu", (unsigned long long)pid);
383 bool studio_is_loaded(void)
385 return g_studio.dbus_object != NULL;
388 bool studio_is_started(void)
390 return ladish_environment_get(&g_studio.env_store, ladish_environment_jack_server_started);
393 bool studio_compose_filename(const char * name, char ** filename_ptr_ptr, char ** backup_filename_ptr_ptr)
395 size_t len_dir;
396 char * p;
397 const char * src;
398 char * filename_ptr;
399 char * backup_filename_ptr = NULL;
401 len_dir = strlen(g_studios_dir);
403 filename_ptr = malloc(len_dir + 1 + strlen(name) * 3 + 4 + 1);
404 if (filename_ptr == NULL)
406 log_error("malloc failed to allocate memory for studio file path");
407 return false;
410 if (backup_filename_ptr_ptr != NULL)
412 backup_filename_ptr = malloc(len_dir + 1 + strlen(name) * 3 + 4 + 4 + 1);
413 if (backup_filename_ptr == NULL)
415 log_error("malloc failed to allocate memory for studio backup file path");
416 free(filename_ptr);
417 return false;
421 p = filename_ptr;
422 memcpy(p, g_studios_dir, len_dir);
423 p += len_dir;
425 *p++ = '/';
427 src = name;
428 escape(&src, &p);
429 strcpy(p, ".xml");
431 *filename_ptr_ptr = filename_ptr;
433 if (backup_filename_ptr_ptr != NULL)
435 strcpy(backup_filename_ptr, filename_ptr);
436 strcat(backup_filename_ptr, ".bak");
437 *backup_filename_ptr_ptr = backup_filename_ptr;
440 return true;
443 bool studios_iterate(void * call_ptr, void * context, bool (* callback)(void * call_ptr, void * context, const char * studio, uint32_t modtime))
445 DIR * dir;
446 struct dirent * dentry;
447 size_t len;
448 struct stat st;
449 char * path;
450 char * name;
452 dir = opendir(g_studios_dir);
453 if (dir == NULL)
455 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Cannot open directory '%s': %d (%s)", g_studios_dir, errno, strerror(errno));
456 return false;
459 while ((dentry = readdir(dir)) != NULL)
461 if (dentry->d_type != DT_REG)
462 continue;
464 len = strlen(dentry->d_name);
465 if (len <= 4 || strcmp(dentry->d_name + (len - 4), ".xml") != 0)
466 continue;
468 path = catdup(g_studios_dir, dentry->d_name);
469 if (path == NULL)
471 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "catdup() failed");
472 return false;
475 if (stat(path, &st) != 0)
477 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "failed to stat '%s': %d (%s)", path, errno, strerror(errno));
478 free(path);
479 return false;
482 free(path);
484 name = malloc(len - 4 + 1);
485 if (name == NULL)
487 log_error("malloc() failed.");
488 closedir(dir);
489 return false;
492 name[unescape(dentry->d_name, len - 4, name)] = 0;
493 //log_info("name = '%s'", name);
495 if (!callback(call_ptr, context, name, st.st_mtime))
497 free(name);
498 closedir(dir);
499 return false;
502 free(name);
505 closedir(dir);
506 return true;
509 bool studio_delete(void * call_ptr, const char * studio_name)
511 char * filename;
512 char * bak_filename;
513 struct stat st;
514 bool ret;
516 ret = false;
518 if (!studio_compose_filename(studio_name, &filename, &bak_filename))
520 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "failed to compose studio filename");
521 goto exit;
524 log_info("Deleting studio ('%s')", filename);
526 if (unlink(filename) != 0)
528 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "unlink(%s) failed: %d (%s)", filename, errno, strerror(errno));
529 goto free;
532 /* try to delete the backup file */
533 if (stat(bak_filename, &st) == 0)
535 if (unlink(bak_filename) != 0)
537 /* failing to delete backup file will not case delete command failure */
538 log_error("unlink(%s) failed: %d (%s)", bak_filename, errno, strerror(errno));
542 ret = true;
544 free:
545 free(filename);
546 free(bak_filename);
547 exit:
548 return ret;
551 void emit_studio_renamed()
553 dbus_signal_emit(g_dbus_connection, STUDIO_OBJECT_PATH, IFACE_STUDIO, "StudioRenamed", "s", &g_studio.name);
556 static void ladish_get_studio_name(struct dbus_method_call * call_ptr)
558 method_return_new_single(call_ptr, DBUS_TYPE_STRING, &g_studio.name);
561 static void ladish_rename_studio(struct dbus_method_call * call_ptr)
563 const char * new_name;
564 char * new_name_dup;
566 if (!dbus_message_get_args(call_ptr->message, &g_dbus_error, DBUS_TYPE_STRING, &new_name, DBUS_TYPE_INVALID))
568 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
569 dbus_error_free(&g_dbus_error);
570 return;
573 log_info("Rename studio request (%s)", new_name);
575 new_name_dup = strdup(new_name);
576 if (new_name_dup == NULL)
578 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "strdup() failed to allocate new name.");
579 return;
582 free(g_studio.name);
583 g_studio.name = new_name_dup;
585 method_return_new_void(call_ptr);
586 emit_studio_renamed();
589 static void ladish_save_studio(struct dbus_method_call * call_ptr)
591 log_info("Save studio request");
593 /* FIXME: this is wrong place to do such check because state before
594 command execution needs to be checked and not state before
595 command is submited, but doing it here will show error to
596 user. Once notification mechanism is implemented, the
597 studio_is_started() check in save command run menthod
598 will send a notification and this check must be removed. */
599 if (!studio_is_started())
601 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Cannot save not-started studio");
602 return;
605 if (ladish_command_save_studio(call_ptr, &g_studio.cmd_queue))
607 method_return_new_void(call_ptr);
611 static void ladish_unload_studio(struct dbus_method_call * call_ptr)
613 log_info("Unload studio request");
615 if (ladish_command_unload_studio(call_ptr, &g_studio.cmd_queue))
617 method_return_new_void(call_ptr);
621 static void ladish_stop_studio(struct dbus_method_call * call_ptr)
623 log_info("Stop studio request");
625 g_studio.automatic = false; /* even if it was automatic, it is not anymore because user knows about it */
627 if (ladish_command_stop_studio(call_ptr, &g_studio.cmd_queue))
629 method_return_new_void(call_ptr);
633 static void ladish_start_studio(struct dbus_method_call * call_ptr)
635 log_info("Start studio request");
637 g_studio.automatic = false; /* even if it was automatic, it is not anymore because user knows about it */
639 if (ladish_command_start_studio(call_ptr, &g_studio.cmd_queue))
641 method_return_new_void(call_ptr);
645 static void ladish_studio_is_started(struct dbus_method_call * call_ptr)
647 dbus_bool_t started;
649 started = g_studio.jack_graph_proxy != NULL;
651 method_return_new_single(call_ptr, DBUS_TYPE_BOOLEAN, &started);
654 METHOD_ARGS_BEGIN(GetName, "Get studio name")
655 METHOD_ARG_DESCRIBE_OUT("studio_name", "s", "Name of studio")
656 METHOD_ARGS_END
658 METHOD_ARGS_BEGIN(Rename, "Rename studio")
659 METHOD_ARG_DESCRIBE_IN("studio_name", "s", "New name")
660 METHOD_ARGS_END
662 METHOD_ARGS_BEGIN(Save, "Save studio")
663 METHOD_ARGS_END
665 METHOD_ARGS_BEGIN(Unload, "Unload studio")
666 METHOD_ARGS_END
668 METHOD_ARGS_BEGIN(Start, "Start studio")
669 METHOD_ARGS_END
671 METHOD_ARGS_BEGIN(Stop, "Stop studio")
672 METHOD_ARGS_END
674 METHOD_ARGS_BEGIN(IsStarted, "Check whether studio is started")
675 METHOD_ARG_DESCRIBE_OUT("started", "b", "Whether studio is started")
676 METHOD_ARGS_END
678 METHODS_BEGIN
679 METHOD_DESCRIBE(GetName, ladish_get_studio_name)
680 METHOD_DESCRIBE(Rename, ladish_rename_studio)
681 METHOD_DESCRIBE(Save, ladish_save_studio)
682 METHOD_DESCRIBE(Unload, ladish_unload_studio)
683 METHOD_DESCRIBE(Start, ladish_start_studio)
684 METHOD_DESCRIBE(Stop, ladish_stop_studio)
685 METHOD_DESCRIBE(IsStarted, ladish_studio_is_started)
686 METHODS_END
688 SIGNAL_ARGS_BEGIN(StudioRenamed, "Studio name changed")
689 SIGNAL_ARG_DESCRIBE("studio_name", "s", "New studio name")
690 SIGNAL_ARGS_END
692 SIGNAL_ARGS_BEGIN(StudioStarted, "Studio started")
693 SIGNAL_ARGS_END
695 SIGNAL_ARGS_BEGIN(StudioCrashed, "Studio crashed")
696 SIGNAL_ARGS_END
698 SIGNAL_ARGS_BEGIN(StudioStopped, "Studio stopped")
699 SIGNAL_ARGS_END
701 SIGNAL_ARGS_BEGIN(RoomAppeared, "Room D-Bus object appeared")
702 SIGNAL_ARG_DESCRIBE("room_path", "s", "room object path")
703 SIGNAL_ARGS_END
705 SIGNAL_ARGS_BEGIN(RoomDisappeared, "Room D-Bus object disappeared")
706 SIGNAL_ARG_DESCRIBE("room_path", "s", "room object path")
707 SIGNAL_ARGS_END
709 SIGNALS_BEGIN
710 SIGNAL_DESCRIBE(StudioRenamed)
711 SIGNAL_DESCRIBE(StudioStarted)
712 SIGNAL_DESCRIBE(StudioCrashed)
713 SIGNAL_DESCRIBE(StudioStopped)
714 SIGNAL_DESCRIBE(RoomAppeared)
715 SIGNAL_DESCRIBE(RoomDisappeared)
716 SIGNALS_END
718 INTERFACE_BEGIN(g_interface_studio, IFACE_STUDIO)
719 INTERFACE_DEFAULT_HANDLER
720 INTERFACE_EXPOSE_METHODS
721 INTERFACE_EXPOSE_SIGNALS
722 INTERFACE_END