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 if (ladish_command_save_studio(call_ptr
, &g_studio
.cmd_queue
))
545 method_return_new_void(call_ptr
);
549 static void ladish_unload_studio(struct dbus_method_call
* call_ptr
)
551 log_info("Unload studio request");
553 if (ladish_command_unload_studio(call_ptr
, &g_studio
.cmd_queue
))
555 method_return_new_void(call_ptr
);
559 static void ladish_stop_studio(struct dbus_method_call
* call_ptr
)
561 log_info("Stop studio request");
563 g_studio
.automatic
= false; /* even if it was automatic, it is not anymore because user knows about it */
565 if (ladish_command_stop_studio(call_ptr
, &g_studio
.cmd_queue
))
567 method_return_new_void(call_ptr
);
571 static void ladish_start_studio(struct dbus_method_call
* call_ptr
)
573 log_info("Start 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_start_studio(call_ptr
, &g_studio
.cmd_queue
))
579 method_return_new_void(call_ptr
);
583 METHOD_ARGS_BEGIN(GetName
, "Get studio name")
584 METHOD_ARG_DESCRIBE_OUT("studio_name", "s", "Name of studio")
587 METHOD_ARGS_BEGIN(Rename
, "Rename studio")
588 METHOD_ARG_DESCRIBE_IN("studio_name", "s", "New name")
591 METHOD_ARGS_BEGIN(Save
, "Save studio")
594 METHOD_ARGS_BEGIN(Unload
, "Unload studio")
597 METHOD_ARGS_BEGIN(Start
, "Start studio")
600 METHOD_ARGS_BEGIN(Stop
, "Stop studio")
604 METHOD_DESCRIBE(GetName
, ladish_get_studio_name
)
605 METHOD_DESCRIBE(Rename
, ladish_rename_studio
)
606 METHOD_DESCRIBE(Save
, ladish_save_studio
)
607 METHOD_DESCRIBE(Unload
, ladish_unload_studio
)
608 METHOD_DESCRIBE(Start
, ladish_start_studio
)
609 METHOD_DESCRIBE(Stop
, ladish_stop_studio
)
612 SIGNAL_ARGS_BEGIN(StudioRenamed
, "Studio name changed")
613 SIGNAL_ARG_DESCRIBE("studio_name", "s", "New studio name")
616 SIGNAL_ARGS_BEGIN(StudioStarted
, "Studio started")
619 SIGNAL_ARGS_BEGIN(StudioStopped
, "Studio stopped")
622 SIGNAL_ARGS_BEGIN(RoomAppeared
, "Room D-Bus object appeared")
623 SIGNAL_ARG_DESCRIBE("room_path", "s", "room object path")
626 SIGNAL_ARGS_BEGIN(RoomDisappeared
, "Room D-Bus object disappeared")
627 SIGNAL_ARG_DESCRIBE("room_path", "s", "room object path")
631 SIGNAL_DESCRIBE(StudioRenamed
)
632 SIGNAL_DESCRIBE(StudioStarted
)
633 SIGNAL_DESCRIBE(StudioStopped
)
634 SIGNAL_DESCRIBE(RoomAppeared
)
635 SIGNAL_DESCRIBE(RoomDisappeared
)
638 INTERFACE_BEGIN(g_interface_studio
, IFACE_STUDIO
)
639 INTERFACE_DEFAULT_HANDLER
640 INTERFACE_EXPOSE_METHODS
641 INTERFACE_EXPOSE_SIGNALS