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()
108 dbus_signal_emit(g_dbus_connection
, STUDIO_OBJECT_PATH
, IFACE_STUDIO
, "StudioStarted", "");
111 void emit_studio_stopped()
113 dbus_signal_emit(g_dbus_connection
, STUDIO_OBJECT_PATH
, IFACE_STUDIO
, "StudioStopped", "");
116 void on_event_jack_started(void)
118 if (g_studio
.dbus_object
== NULL
)
120 ASSERT(g_studio
.name
== NULL
);
121 if (!studio_name_generate(&g_studio
.name
))
123 log_error("studio_name_generate() failed.");
127 g_studio
.automatic
= true;
132 if (!studio_fetch_jack_settings())
134 log_error("studio_fetch_jack_settings() failed.");
139 log_info("jack conf successfully retrieved");
140 g_studio
.jack_conf_valid
= true;
142 if (!graph_proxy_create(JACKDBUS_SERVICE_NAME
, JACKDBUS_OBJECT_PATH
, false, &g_studio
.jack_graph_proxy
))
144 log_error("graph_proxy_create() failed for jackdbus");
148 if (!ladish_virtualizer_create(g_studio
.jack_graph_proxy
, g_studio
.jack_graph
, g_studio
.studio_graph
, &g_studio
.virtualizer
))
150 log_error("ladish_virtualizer_create() failed.");
153 if (!graph_proxy_activate(g_studio
.jack_graph_proxy
))
155 log_error("graph_proxy_activate() failed.");
159 ladish_app_supervisor_autorun(g_studio
.app_supervisor
);
161 emit_studio_started();
164 void on_event_jack_stopped(void)
166 emit_studio_stopped();
168 if (g_studio
.automatic
)
170 log_info("Unloading automatic studio.");
171 ladish_command_unload_studio(NULL
, &g_studio
.cmd_queue
);
175 if (g_studio
.virtualizer
)
177 ladish_virtualizer_destroy(g_studio
.virtualizer
);
178 g_studio
.virtualizer
= NULL
;
181 if (g_studio
.jack_graph_proxy
)
183 graph_proxy_destroy(g_studio
.jack_graph_proxy
);
184 g_studio
.jack_graph_proxy
= NULL
;
187 /* TODO: if user wants, restart jack server and reconnect all jack apps to it */
190 void studio_run(void)
194 ladish_cqueue_run(&g_studio
.cmd_queue
);
196 { /* if quit is requested, don't bother to process external events */
200 if (ladish_environment_consume_change(&g_studio
.env_store
, ladish_environment_jack_server_started
, &state
))
202 ladish_cqueue_clear(&g_studio
.cmd_queue
);
206 on_event_jack_started();
210 on_event_jack_stopped();
214 ladish_environment_ignore(&g_studio
.env_store
, ladish_environment_jack_server_present
);
215 ladish_environment_assert_consumed(&g_studio
.env_store
);
218 static void on_jack_server_started(void)
220 log_info("JACK server start detected.");
221 ladish_environment_set(&g_studio
.env_store
, ladish_environment_jack_server_started
);
224 static void on_jack_server_stopped(void)
226 ladish_environment_reset(&g_studio
.env_store
, ladish_environment_jack_server_started
);
229 static void on_jack_server_appeared(void)
231 log_info("JACK controller appeared.");
232 ladish_environment_set(&g_studio
.env_store
, ladish_environment_jack_server_present
);
235 static void on_jack_server_disappeared(void)
237 log_info("JACK controller disappeared.");
238 ladish_environment_reset(&g_studio
.env_store
, ladish_environment_jack_server_present
);
241 bool studio_init(void)
243 log_info("studio object construct");
245 g_studios_dir
= catdup(g_base_dir
, STUDIOS_DIR
);
246 if (g_studios_dir
== NULL
)
248 log_error("catdup failed for '%s' and '%s'", g_base_dir
, STUDIOS_DIR
);
252 if (!ensure_dir_exist(g_studios_dir
, 0700))
254 goto free_studios_dir
;
257 INIT_LIST_HEAD(&g_studio
.all_connections
);
258 INIT_LIST_HEAD(&g_studio
.all_ports
);
259 INIT_LIST_HEAD(&g_studio
.all_clients
);
260 INIT_LIST_HEAD(&g_studio
.jack_connections
);
261 INIT_LIST_HEAD(&g_studio
.jack_ports
);
262 INIT_LIST_HEAD(&g_studio
.jack_clients
);
263 INIT_LIST_HEAD(&g_studio
.rooms
);
264 INIT_LIST_HEAD(&g_studio
.clients
);
265 INIT_LIST_HEAD(&g_studio
.ports
);
267 INIT_LIST_HEAD(&g_studio
.jack_conf
);
268 INIT_LIST_HEAD(&g_studio
.jack_params
);
270 g_studio
.dbus_object
= NULL
;
271 g_studio
.name
= NULL
;
272 g_studio
.filename
= NULL
;
274 if (!ladish_graph_create(&g_studio
.jack_graph
, NULL
))
276 log_error("ladish_graph_create() failed to create jack graph object.");
277 goto free_studios_dir
;
280 if (!ladish_graph_create(&g_studio
.studio_graph
, STUDIO_OBJECT_PATH
))
282 log_error("ladish_graph_create() failed to create studio graph object.");
283 goto jack_graph_destroy
;
286 if (!ladish_app_supervisor_create(&g_studio
.app_supervisor
, STUDIO_OBJECT_PATH
, "studio"))
288 log_error("ladish_app_supervisor_create() failed.");
289 goto studio_graph_destroy
;
292 ladish_cqueue_init(&g_studio
.cmd_queue
);
293 ladish_environment_init(&g_studio
.env_store
);
295 if (!jack_proxy_init(
296 on_jack_server_started
,
297 on_jack_server_stopped
,
298 on_jack_server_appeared
,
299 on_jack_server_disappeared
))
301 log_error("jack_proxy_init() failed.");
302 goto app_supervisor_destroy
;
307 app_supervisor_destroy
:
308 ladish_app_supervisor_destroy(g_studio
.app_supervisor
);
309 studio_graph_destroy
:
310 ladish_graph_destroy(g_studio
.studio_graph
, false);
312 ladish_graph_destroy(g_studio
.jack_graph
, false);
319 void studio_uninit(void)
321 log_info("studio_uninit()");
325 ladish_cqueue_clear(&g_studio
.cmd_queue
);
329 log_info("studio object destroy");
332 void studio_on_child_exit(pid_t pid
)
334 if (!ladish_app_supervisor_child_exit(g_studio
.app_supervisor
, pid
))
336 log_error("non-studio child exit detected. pid is %llu", (unsigned long long)pid
);
340 bool studio_is_loaded(void)
342 return g_studio
.dbus_object
!= NULL
;
345 bool studio_is_started(void)
347 return ladish_environment_get(&g_studio
.env_store
, ladish_environment_jack_server_started
);
350 bool studio_compose_filename(const char * name
, char ** filename_ptr_ptr
, char ** backup_filename_ptr_ptr
)
356 char * backup_filename_ptr
= NULL
;
358 len_dir
= strlen(g_studios_dir
);
360 filename_ptr
= malloc(len_dir
+ 1 + strlen(name
) * 3 + 4 + 1);
361 if (filename_ptr
== NULL
)
363 log_error("malloc failed to allocate memory for studio file path");
367 if (backup_filename_ptr_ptr
!= NULL
)
369 backup_filename_ptr
= malloc(len_dir
+ 1 + strlen(name
) * 3 + 4 + 4 + 1);
370 if (backup_filename_ptr
== NULL
)
372 log_error("malloc failed to allocate memory for studio backup file path");
379 memcpy(p
, g_studios_dir
, len_dir
);
388 *filename_ptr_ptr
= filename_ptr
;
390 if (backup_filename_ptr_ptr
!= NULL
)
392 strcpy(backup_filename_ptr
, filename_ptr
);
393 strcat(backup_filename_ptr
, ".bak");
394 *backup_filename_ptr_ptr
= backup_filename_ptr
;
400 bool studios_iterate(void * call_ptr
, void * context
, bool (* callback
)(void * call_ptr
, void * context
, const char * studio
, uint32_t modtime
))
403 struct dirent
* dentry
;
409 dir
= opendir(g_studios_dir
);
412 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_GENERIC
, "Cannot open directory '%s': %d (%s)", g_studios_dir
, errno
, strerror(errno
));
416 while ((dentry
= readdir(dir
)) != NULL
)
418 if (dentry
->d_type
!= DT_REG
)
421 len
= strlen(dentry
->d_name
);
422 if (len
<= 4 || strcmp(dentry
->d_name
+ (len
- 4), ".xml") != 0)
425 path
= catdup(g_studios_dir
, dentry
->d_name
);
428 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_GENERIC
, "catdup() failed");
432 if (stat(path
, &st
) != 0)
434 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_GENERIC
, "failed to stat '%s': %d (%s)", path
, errno
, strerror(errno
));
441 name
= malloc(len
- 4 + 1);
442 name
[unescape(dentry
->d_name
, len
- 4, name
)] = 0;
443 //log_info("name = '%s'", name);
445 if (!callback(call_ptr
, context
, name
, st
.st_mtime
))
459 bool studio_delete(void * call_ptr
, const char * studio_name
)
468 if (!studio_compose_filename(studio_name
, &filename
, &bak_filename
))
470 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_GENERIC
, "failed to compose studio filename");
474 log_info("Deleting studio ('%s')", filename
);
476 if (unlink(filename
) != 0)
478 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_GENERIC
, "unlink(%s) failed: %d (%s)", filename
, errno
, strerror(errno
));
482 /* try to delete the backup file */
483 if (stat(bak_filename
, &st
) == 0)
485 if (unlink(bak_filename
) != 0)
487 /* failing to delete backup file will not case delete command failure */
488 log_error("unlink(%s) failed: %d (%s)", bak_filename
, errno
, strerror(errno
));
501 void emit_studio_renamed()
503 dbus_signal_emit(g_dbus_connection
, STUDIO_OBJECT_PATH
, IFACE_STUDIO
, "StudioRenamed", "s", &g_studio
.name
);
506 static void ladish_get_studio_name(struct dbus_method_call
* call_ptr
)
508 method_return_new_single(call_ptr
, DBUS_TYPE_STRING
, &g_studio
.name
);
511 static void ladish_rename_studio(struct dbus_method_call
* call_ptr
)
513 const char * new_name
;
516 if (!dbus_message_get_args(call_ptr
->message
, &g_dbus_error
, DBUS_TYPE_STRING
, &new_name
, DBUS_TYPE_INVALID
))
518 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_INVALID_ARGS
, "Invalid arguments to method \"%s\": %s", call_ptr
->method_name
, g_dbus_error
.message
);
519 dbus_error_free(&g_dbus_error
);
523 log_info("Rename studio request (%s)", new_name
);
525 new_name_dup
= strdup(new_name
);
526 if (new_name_dup
== NULL
)
528 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_GENERIC
, "strdup() failed to allocate new name.");
533 g_studio
.name
= new_name_dup
;
535 method_return_new_void(call_ptr
);
536 emit_studio_renamed();
539 static void ladish_save_studio(struct dbus_method_call
* call_ptr
)
541 log_info("Save studio request");
543 /* FIXME: this is wrong place to do such check because state before
544 command execution needs to be checked and not state before
545 command is submited, but doing it here will show error to
546 user. Once notification mechanism is implemented, the
547 studio_is_started() check in save command run menthod
548 will send a notification and this check must be removed. */
549 if (!studio_is_started())
551 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_GENERIC
, "Cannot save not-started studio");
555 if (ladish_command_save_studio(call_ptr
, &g_studio
.cmd_queue
))
557 method_return_new_void(call_ptr
);
561 static void ladish_unload_studio(struct dbus_method_call
* call_ptr
)
563 log_info("Unload studio request");
565 if (ladish_command_unload_studio(call_ptr
, &g_studio
.cmd_queue
))
567 method_return_new_void(call_ptr
);
571 static void ladish_stop_studio(struct dbus_method_call
* call_ptr
)
573 log_info("Stop studio request");
575 g_studio
.automatic
= false; /* even if it was automatic, it is not anymore because user knows about it */
577 if (ladish_command_stop_studio(call_ptr
, &g_studio
.cmd_queue
))
579 method_return_new_void(call_ptr
);
583 static void ladish_start_studio(struct dbus_method_call
* call_ptr
)
585 log_info("Start studio request");
587 g_studio
.automatic
= false; /* even if it was automatic, it is not anymore because user knows about it */
589 if (ladish_command_start_studio(call_ptr
, &g_studio
.cmd_queue
))
591 method_return_new_void(call_ptr
);
595 static void ladish_studio_is_started(struct dbus_method_call
* call_ptr
)
599 started
= g_studio
.jack_graph_proxy
!= NULL
;
601 method_return_new_single(call_ptr
, DBUS_TYPE_BOOLEAN
, &started
);
604 METHOD_ARGS_BEGIN(GetName
, "Get studio name")
605 METHOD_ARG_DESCRIBE_OUT("studio_name", "s", "Name of studio")
608 METHOD_ARGS_BEGIN(Rename
, "Rename studio")
609 METHOD_ARG_DESCRIBE_IN("studio_name", "s", "New name")
612 METHOD_ARGS_BEGIN(Save
, "Save studio")
615 METHOD_ARGS_BEGIN(Unload
, "Unload studio")
618 METHOD_ARGS_BEGIN(Start
, "Start studio")
621 METHOD_ARGS_BEGIN(Stop
, "Stop studio")
624 METHOD_ARGS_BEGIN(IsStarted
, "Check whether studio is started")
625 METHOD_ARG_DESCRIBE_OUT("started", "b", "Whether studio is started")
629 METHOD_DESCRIBE(GetName
, ladish_get_studio_name
)
630 METHOD_DESCRIBE(Rename
, ladish_rename_studio
)
631 METHOD_DESCRIBE(Save
, ladish_save_studio
)
632 METHOD_DESCRIBE(Unload
, ladish_unload_studio
)
633 METHOD_DESCRIBE(Start
, ladish_start_studio
)
634 METHOD_DESCRIBE(Stop
, ladish_stop_studio
)
635 METHOD_DESCRIBE(IsStarted
, ladish_studio_is_started
)
638 SIGNAL_ARGS_BEGIN(StudioRenamed
, "Studio name changed")
639 SIGNAL_ARG_DESCRIBE("studio_name", "s", "New studio name")
642 SIGNAL_ARGS_BEGIN(StudioStarted
, "Studio started")
645 SIGNAL_ARGS_BEGIN(StudioStopped
, "Studio stopped")
648 SIGNAL_ARGS_BEGIN(RoomAppeared
, "Room D-Bus object appeared")
649 SIGNAL_ARG_DESCRIBE("room_path", "s", "room object path")
652 SIGNAL_ARGS_BEGIN(RoomDisappeared
, "Room D-Bus object disappeared")
653 SIGNAL_ARG_DESCRIBE("room_path", "s", "room object path")
657 SIGNAL_DESCRIBE(StudioRenamed
)
658 SIGNAL_DESCRIBE(StudioStarted
)
659 SIGNAL_DESCRIBE(StudioStopped
)
660 SIGNAL_DESCRIBE(RoomAppeared
)
661 SIGNAL_DESCRIBE(RoomDisappeared
)
664 INTERFACE_BEGIN(g_interface_studio
, IFACE_STUDIO
)
665 INTERFACE_DEFAULT_HANDLER
666 INTERFACE_EXPOSE_METHODS
667 INTERFACE_EXPOSE_SIGNALS