notify user when JACK crashes or stops unexpectedly
[ladish.git] / daemon / studio.c
blobf863b6bbccac6be24f74ac545b3e62092c6e0625
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 /* Automatic studio creation on JACK server start */
208 if (g_studio.dbus_object == NULL)
210 ASSERT(g_studio.name == NULL);
211 if (!studio_name_generate(&g_studio.name))
213 log_error("studio_name_generate() failed.");
214 return;
217 g_studio.automatic = true;
219 studio_publish();
222 on_event_jack_started();
224 else
226 /* JACK stopped but this was not expected. When expected.
227 * the change will be consumed by the run method of the studio stop command */
228 log_error("JACK stopped unexpectedly.");
229 log_error("Save your work, then unload and reload the studio.");
230 handle_unexpected_jack_server_stop();
234 if (ladish_environment_consume_change(&g_studio.env_store, ladish_environment_jack_server_present, &state))
236 if (g_studio.jack_graph_proxy != NULL)
238 ladish_cqueue_clear(&g_studio.cmd_queue);
240 /* jack was started, this probably means that jackdbus has crashed */
241 log_error("JACK disappeared unexpectedly. Maybe it crashed.");
242 log_error("Save your work, then unload and reload the studio.");
243 ladish_environment_reset_stealth(&g_studio.env_store, ladish_environment_jack_server_started);
245 ladish_graph_clear(g_studio.studio_graph, false);
246 ladish_graph_clear(g_studio.jack_graph, true);
248 handle_unexpected_jack_server_stop();
252 ladish_environment_assert_consumed(&g_studio.env_store);
255 static void on_jack_server_started(void)
257 log_info("JACK server start detected.");
258 ladish_environment_set(&g_studio.env_store, ladish_environment_jack_server_started);
261 static void on_jack_server_stopped(void)
263 log_info("JACK server stop detected.");
264 ladish_environment_reset(&g_studio.env_store, ladish_environment_jack_server_started);
267 static void on_jack_server_appeared(void)
269 log_info("JACK controller appeared.");
270 ladish_environment_set(&g_studio.env_store, ladish_environment_jack_server_present);
273 static void on_jack_server_disappeared(void)
275 log_info("JACK controller disappeared.");
276 ladish_environment_reset(&g_studio.env_store, ladish_environment_jack_server_present);
279 bool studio_init(void)
281 log_info("studio object construct");
283 g_studios_dir = catdup(g_base_dir, STUDIOS_DIR);
284 if (g_studios_dir == NULL)
286 log_error("catdup failed for '%s' and '%s'", g_base_dir, STUDIOS_DIR);
287 goto fail;
290 if (!ensure_dir_exist(g_studios_dir, 0700))
292 goto free_studios_dir;
295 INIT_LIST_HEAD(&g_studio.all_connections);
296 INIT_LIST_HEAD(&g_studio.all_ports);
297 INIT_LIST_HEAD(&g_studio.all_clients);
298 INIT_LIST_HEAD(&g_studio.jack_connections);
299 INIT_LIST_HEAD(&g_studio.jack_ports);
300 INIT_LIST_HEAD(&g_studio.jack_clients);
301 INIT_LIST_HEAD(&g_studio.rooms);
302 INIT_LIST_HEAD(&g_studio.clients);
303 INIT_LIST_HEAD(&g_studio.ports);
305 INIT_LIST_HEAD(&g_studio.jack_conf);
306 INIT_LIST_HEAD(&g_studio.jack_params);
308 g_studio.dbus_object = NULL;
309 g_studio.name = NULL;
310 g_studio.filename = NULL;
312 if (!ladish_graph_create(&g_studio.jack_graph, NULL))
314 log_error("ladish_graph_create() failed to create jack graph object.");
315 goto free_studios_dir;
318 if (!ladish_graph_create(&g_studio.studio_graph, STUDIO_OBJECT_PATH))
320 log_error("ladish_graph_create() failed to create studio graph object.");
321 goto jack_graph_destroy;
324 if (!ladish_app_supervisor_create(&g_studio.app_supervisor, STUDIO_OBJECT_PATH, "studio"))
326 log_error("ladish_app_supervisor_create() failed.");
327 goto studio_graph_destroy;
330 ladish_cqueue_init(&g_studio.cmd_queue);
331 ladish_environment_init(&g_studio.env_store);
333 if (!jack_proxy_init(
334 on_jack_server_started,
335 on_jack_server_stopped,
336 on_jack_server_appeared,
337 on_jack_server_disappeared))
339 log_error("jack_proxy_init() failed.");
340 goto app_supervisor_destroy;
343 return true;
345 app_supervisor_destroy:
346 ladish_app_supervisor_destroy(g_studio.app_supervisor);
347 studio_graph_destroy:
348 ladish_graph_destroy(g_studio.studio_graph, false);
349 jack_graph_destroy:
350 ladish_graph_destroy(g_studio.jack_graph, false);
351 free_studios_dir:
352 free(g_studios_dir);
353 fail:
354 return false;
357 void studio_uninit(void)
359 log_info("studio_uninit()");
361 jack_proxy_uninit();
363 ladish_cqueue_clear(&g_studio.cmd_queue);
365 ladish_graph_destroy(g_studio.studio_graph, false);
366 ladish_graph_destroy(g_studio.jack_graph, false);
368 free(g_studios_dir);
370 log_info("studio object destroy");
373 void studio_on_child_exit(pid_t pid)
375 if (!ladish_app_supervisor_child_exit(g_studio.app_supervisor, pid))
377 log_error("non-studio child exit detected. pid is %llu", (unsigned long long)pid);
381 bool studio_is_loaded(void)
383 return g_studio.dbus_object != NULL;
386 bool studio_is_started(void)
388 return ladish_environment_get(&g_studio.env_store, ladish_environment_jack_server_started);
391 bool studio_compose_filename(const char * name, char ** filename_ptr_ptr, char ** backup_filename_ptr_ptr)
393 size_t len_dir;
394 char * p;
395 const char * src;
396 char * filename_ptr;
397 char * backup_filename_ptr = NULL;
399 len_dir = strlen(g_studios_dir);
401 filename_ptr = malloc(len_dir + 1 + strlen(name) * 3 + 4 + 1);
402 if (filename_ptr == NULL)
404 log_error("malloc failed to allocate memory for studio file path");
405 return false;
408 if (backup_filename_ptr_ptr != NULL)
410 backup_filename_ptr = malloc(len_dir + 1 + strlen(name) * 3 + 4 + 4 + 1);
411 if (backup_filename_ptr == NULL)
413 log_error("malloc failed to allocate memory for studio backup file path");
414 free(filename_ptr);
415 return false;
419 p = filename_ptr;
420 memcpy(p, g_studios_dir, len_dir);
421 p += len_dir;
423 *p++ = '/';
425 src = name;
426 escape(&src, &p);
427 strcpy(p, ".xml");
429 *filename_ptr_ptr = filename_ptr;
431 if (backup_filename_ptr_ptr != NULL)
433 strcpy(backup_filename_ptr, filename_ptr);
434 strcat(backup_filename_ptr, ".bak");
435 *backup_filename_ptr_ptr = backup_filename_ptr;
438 return true;
441 bool studios_iterate(void * call_ptr, void * context, bool (* callback)(void * call_ptr, void * context, const char * studio, uint32_t modtime))
443 DIR * dir;
444 struct dirent * dentry;
445 size_t len;
446 struct stat st;
447 char * path;
448 char * name;
450 dir = opendir(g_studios_dir);
451 if (dir == NULL)
453 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Cannot open directory '%s': %d (%s)", g_studios_dir, errno, strerror(errno));
454 return false;
457 while ((dentry = readdir(dir)) != NULL)
459 if (dentry->d_type != DT_REG)
460 continue;
462 len = strlen(dentry->d_name);
463 if (len <= 4 || strcmp(dentry->d_name + (len - 4), ".xml") != 0)
464 continue;
466 path = catdup(g_studios_dir, dentry->d_name);
467 if (path == NULL)
469 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "catdup() failed");
470 return false;
473 if (stat(path, &st) != 0)
475 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "failed to stat '%s': %d (%s)", path, errno, strerror(errno));
476 free(path);
477 return false;
480 free(path);
482 name = malloc(len - 4 + 1);
483 if (name == NULL)
485 log_error("malloc() failed.");
486 closedir(dir);
487 return false;
490 name[unescape(dentry->d_name, len - 4, name)] = 0;
491 //log_info("name = '%s'", name);
493 if (!callback(call_ptr, context, name, st.st_mtime))
495 free(name);
496 closedir(dir);
497 return false;
500 free(name);
503 closedir(dir);
504 return true;
507 bool studio_delete(void * call_ptr, const char * studio_name)
509 char * filename;
510 char * bak_filename;
511 struct stat st;
512 bool ret;
514 ret = false;
516 if (!studio_compose_filename(studio_name, &filename, &bak_filename))
518 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "failed to compose studio filename");
519 goto exit;
522 log_info("Deleting studio ('%s')", filename);
524 if (unlink(filename) != 0)
526 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "unlink(%s) failed: %d (%s)", filename, errno, strerror(errno));
527 goto free;
530 /* try to delete the backup file */
531 if (stat(bak_filename, &st) == 0)
533 if (unlink(bak_filename) != 0)
535 /* failing to delete backup file will not case delete command failure */
536 log_error("unlink(%s) failed: %d (%s)", bak_filename, errno, strerror(errno));
540 ret = true;
542 free:
543 free(filename);
544 free(bak_filename);
545 exit:
546 return ret;
549 void emit_studio_renamed()
551 dbus_signal_emit(g_dbus_connection, STUDIO_OBJECT_PATH, IFACE_STUDIO, "StudioRenamed", "s", &g_studio.name);
554 static void ladish_get_studio_name(struct dbus_method_call * call_ptr)
556 method_return_new_single(call_ptr, DBUS_TYPE_STRING, &g_studio.name);
559 static void ladish_rename_studio(struct dbus_method_call * call_ptr)
561 const char * new_name;
562 char * new_name_dup;
564 if (!dbus_message_get_args(call_ptr->message, &g_dbus_error, DBUS_TYPE_STRING, &new_name, DBUS_TYPE_INVALID))
566 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
567 dbus_error_free(&g_dbus_error);
568 return;
571 log_info("Rename studio request (%s)", new_name);
573 new_name_dup = strdup(new_name);
574 if (new_name_dup == NULL)
576 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "strdup() failed to allocate new name.");
577 return;
580 free(g_studio.name);
581 g_studio.name = new_name_dup;
583 method_return_new_void(call_ptr);
584 emit_studio_renamed();
587 static void ladish_save_studio(struct dbus_method_call * call_ptr)
589 log_info("Save studio request");
591 /* FIXME: this is wrong place to do such check because state before
592 command execution needs to be checked and not state before
593 command is submited, but doing it here will show error to
594 user. Once notification mechanism is implemented, the
595 studio_is_started() check in save command run menthod
596 will send a notification and this check must be removed. */
597 if (!studio_is_started())
599 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Cannot save not-started studio");
600 return;
603 if (ladish_command_save_studio(call_ptr, &g_studio.cmd_queue))
605 method_return_new_void(call_ptr);
609 static void ladish_unload_studio(struct dbus_method_call * call_ptr)
611 log_info("Unload studio request");
613 if (ladish_command_unload_studio(call_ptr, &g_studio.cmd_queue))
615 method_return_new_void(call_ptr);
619 static void ladish_stop_studio(struct dbus_method_call * call_ptr)
621 log_info("Stop studio request");
623 g_studio.automatic = false; /* even if it was automatic, it is not anymore because user knows about it */
625 if (ladish_command_stop_studio(call_ptr, &g_studio.cmd_queue))
627 method_return_new_void(call_ptr);
631 static void ladish_start_studio(struct dbus_method_call * call_ptr)
633 log_info("Start studio request");
635 g_studio.automatic = false; /* even if it was automatic, it is not anymore because user knows about it */
637 if (ladish_command_start_studio(call_ptr, &g_studio.cmd_queue))
639 method_return_new_void(call_ptr);
643 static void ladish_studio_is_started(struct dbus_method_call * call_ptr)
645 dbus_bool_t started;
647 started = g_studio.jack_graph_proxy != NULL;
649 method_return_new_single(call_ptr, DBUS_TYPE_BOOLEAN, &started);
652 METHOD_ARGS_BEGIN(GetName, "Get studio name")
653 METHOD_ARG_DESCRIBE_OUT("studio_name", "s", "Name of studio")
654 METHOD_ARGS_END
656 METHOD_ARGS_BEGIN(Rename, "Rename studio")
657 METHOD_ARG_DESCRIBE_IN("studio_name", "s", "New name")
658 METHOD_ARGS_END
660 METHOD_ARGS_BEGIN(Save, "Save studio")
661 METHOD_ARGS_END
663 METHOD_ARGS_BEGIN(Unload, "Unload studio")
664 METHOD_ARGS_END
666 METHOD_ARGS_BEGIN(Start, "Start studio")
667 METHOD_ARGS_END
669 METHOD_ARGS_BEGIN(Stop, "Stop studio")
670 METHOD_ARGS_END
672 METHOD_ARGS_BEGIN(IsStarted, "Check whether studio is started")
673 METHOD_ARG_DESCRIBE_OUT("started", "b", "Whether studio is started")
674 METHOD_ARGS_END
676 METHODS_BEGIN
677 METHOD_DESCRIBE(GetName, ladish_get_studio_name)
678 METHOD_DESCRIBE(Rename, ladish_rename_studio)
679 METHOD_DESCRIBE(Save, ladish_save_studio)
680 METHOD_DESCRIBE(Unload, ladish_unload_studio)
681 METHOD_DESCRIBE(Start, ladish_start_studio)
682 METHOD_DESCRIBE(Stop, ladish_stop_studio)
683 METHOD_DESCRIBE(IsStarted, ladish_studio_is_started)
684 METHODS_END
686 SIGNAL_ARGS_BEGIN(StudioRenamed, "Studio name changed")
687 SIGNAL_ARG_DESCRIBE("studio_name", "s", "New studio name")
688 SIGNAL_ARGS_END
690 SIGNAL_ARGS_BEGIN(StudioStarted, "Studio started")
691 SIGNAL_ARGS_END
693 SIGNAL_ARGS_BEGIN(StudioCrashed, "Studio crashed")
694 SIGNAL_ARGS_END
696 SIGNAL_ARGS_BEGIN(StudioStopped, "Studio stopped")
697 SIGNAL_ARGS_END
699 SIGNAL_ARGS_BEGIN(RoomAppeared, "Room D-Bus object appeared")
700 SIGNAL_ARG_DESCRIBE("room_path", "s", "room object path")
701 SIGNAL_ARGS_END
703 SIGNAL_ARGS_BEGIN(RoomDisappeared, "Room D-Bus object disappeared")
704 SIGNAL_ARG_DESCRIBE("room_path", "s", "room object path")
705 SIGNAL_ARGS_END
707 SIGNALS_BEGIN
708 SIGNAL_DESCRIBE(StudioRenamed)
709 SIGNAL_DESCRIBE(StudioStarted)
710 SIGNAL_DESCRIBE(StudioCrashed)
711 SIGNAL_DESCRIBE(StudioStopped)
712 SIGNAL_DESCRIBE(RoomAppeared)
713 SIGNAL_DESCRIBE(RoomDisappeared)
714 SIGNALS_END
716 INTERFACE_BEGIN(g_interface_studio, IFACE_STUDIO)
717 INTERFACE_DEFAULT_HANDLER
718 INTERFACE_EXPOSE_METHODS
719 INTERFACE_EXPOSE_SIGNALS
720 INTERFACE_END