1 /* -*- Mode: C ; c-basic-offset: 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.
30 #include <sys/types.h>
35 #include "studio_internal.h"
36 #include "../dbus_constants.h"
38 #include "../catdup.h"
39 #include "dirhelpers.h"
40 #include "graph_dict.h"
43 #define STUDIOS_DIR "/studios/"
46 struct studio g_studio
;
48 bool studio_name_generate(char ** name_ptr
)
51 char timestamp_str
[26];
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
);
62 log_error("catdup failed to create studio name");
73 dbus_object_path object
;
75 ASSERT(g_studio
.name
!= NULL
);
77 object
= dbus_object_path_new(
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
,
86 log_error("dbus_object_path_new() failed");
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
);
97 log_info("Studio D-Bus object created. \"%s\"", g_studio
.name
);
99 g_studio
.dbus_object
= object
;
101 emit_studio_appeared();
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.");
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");
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
);
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)
195 ladish_cqueue_run(&g_studio
.cmd_queue
);
197 { /* if quit is requested, don't bother to process external events */
201 if (ladish_environment_consume_change(&g_studio
.env_store
, ladish_environment_jack_server_started
, &state
))
203 ladish_cqueue_clear(&g_studio
.cmd_queue
);
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.");
219 g_studio
.automatic
= true;
224 on_event_jack_started();
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
);
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
;
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);
352 ladish_graph_destroy(g_studio
.jack_graph
, false);
359 void studio_uninit(void)
361 log_info("studio_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);
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
)
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");
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");
422 memcpy(p
, g_studios_dir
, len_dir
);
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
;
443 bool studios_iterate(void * call_ptr
, void * context
, bool (* callback
)(void * call_ptr
, void * context
, const char * studio
, uint32_t modtime
))
446 struct dirent
* dentry
;
452 dir
= opendir(g_studios_dir
);
455 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_GENERIC
, "Cannot open directory '%s': %d (%s)", g_studios_dir
, errno
, strerror(errno
));
459 while ((dentry
= readdir(dir
)) != NULL
)
461 if (dentry
->d_type
!= DT_REG
)
464 len
= strlen(dentry
->d_name
);
465 if (len
<= 4 || strcmp(dentry
->d_name
+ (len
- 4), ".xml") != 0)
468 path
= catdup(g_studios_dir
, dentry
->d_name
);
471 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_GENERIC
, "catdup() failed");
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
));
484 name
= malloc(len
- 4 + 1);
487 log_error("malloc() failed.");
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
))
509 bool studio_delete(void * call_ptr
, const char * studio_name
)
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");
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
));
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
));
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
;
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
);
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.");
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");
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
)
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")
658 METHOD_ARGS_BEGIN(Rename
, "Rename studio")
659 METHOD_ARG_DESCRIBE_IN("studio_name", "s", "New name")
662 METHOD_ARGS_BEGIN(Save
, "Save studio")
665 METHOD_ARGS_BEGIN(Unload
, "Unload studio")
668 METHOD_ARGS_BEGIN(Start
, "Start studio")
671 METHOD_ARGS_BEGIN(Stop
, "Stop studio")
674 METHOD_ARGS_BEGIN(IsStarted
, "Check whether studio is started")
675 METHOD_ARG_DESCRIBE_OUT("started", "b", "Whether studio is started")
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
)
688 SIGNAL_ARGS_BEGIN(StudioRenamed
, "Studio name changed")
689 SIGNAL_ARG_DESCRIBE("studio_name", "s", "New studio name")
692 SIGNAL_ARGS_BEGIN(StudioStarted
, "Studio started")
695 SIGNAL_ARGS_BEGIN(StudioCrashed
, "Studio crashed")
698 SIGNAL_ARGS_BEGIN(StudioStopped
, "Studio stopped")
701 SIGNAL_ARGS_BEGIN(RoomAppeared
, "Room D-Bus object appeared")
702 SIGNAL_ARG_DESCRIBE("room_path", "s", "room object path")
705 SIGNAL_ARGS_BEGIN(RoomDisappeared
, "Room D-Bus object disappeared")
706 SIGNAL_ARG_DESCRIBE("room_path", "s", "room object path")
710 SIGNAL_DESCRIBE(StudioRenamed
)
711 SIGNAL_DESCRIBE(StudioStarted
)
712 SIGNAL_DESCRIBE(StudioCrashed
)
713 SIGNAL_DESCRIBE(StudioStopped
)
714 SIGNAL_DESCRIBE(RoomAppeared
)
715 SIGNAL_DESCRIBE(RoomDisappeared
)
718 INTERFACE_BEGIN(g_interface_studio
, IFACE_STUDIO
)
719 INTERFACE_DEFAULT_HANDLER
720 INTERFACE_EXPOSE_METHODS
721 INTERFACE_EXPOSE_SIGNALS