daemon: new studio method: IsStarted()
[ladish.git] / daemon / studio.c
blob35f25d9fecd2193b606ea2f47fbe86c82407207a
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()
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.");
124 return;
127 g_studio.automatic = true;
129 studio_publish();
132 if (!studio_fetch_jack_settings())
134 log_error("studio_fetch_jack_settings() failed.");
136 return;
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");
146 else
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);
172 return;
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)
192 bool state;
194 ladish_cqueue_run(&g_studio.cmd_queue);
195 if (g_quit)
196 { /* if quit is requested, don't bother to process external events */
197 return;
200 if (ladish_environment_consume_change(&g_studio.env_store, ladish_environment_jack_server_started, &state))
202 ladish_cqueue_clear(&g_studio.cmd_queue);
204 if (state)
206 on_event_jack_started();
208 else
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);
249 goto fail;
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;
305 return true;
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);
311 jack_graph_destroy:
312 ladish_graph_destroy(g_studio.jack_graph, false);
313 free_studios_dir:
314 free(g_studios_dir);
315 fail:
316 return false;
319 void studio_uninit(void)
321 log_info("studio_uninit()");
323 jack_proxy_uninit();
325 ladish_cqueue_clear(&g_studio.cmd_queue);
327 free(g_studios_dir);
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)
352 size_t len_dir;
353 char * p;
354 const char * src;
355 char * filename_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");
364 return false;
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");
373 free(filename_ptr);
374 return false;
378 p = filename_ptr;
379 memcpy(p, g_studios_dir, len_dir);
380 p += len_dir;
382 *p++ = '/';
384 src = name;
385 escape(&src, &p);
386 strcpy(p, ".xml");
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;
397 return true;
400 bool studios_iterate(void * call_ptr, void * context, bool (* callback)(void * call_ptr, void * context, const char * studio, uint32_t modtime))
402 DIR * dir;
403 struct dirent * dentry;
404 size_t len;
405 struct stat st;
406 char * path;
407 char * name;
409 dir = opendir(g_studios_dir);
410 if (dir == NULL)
412 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Cannot open directory '%s': %d (%s)", g_studios_dir, errno, strerror(errno));
413 return false;
416 while ((dentry = readdir(dir)) != NULL)
418 if (dentry->d_type != DT_REG)
419 continue;
421 len = strlen(dentry->d_name);
422 if (len <= 4 || strcmp(dentry->d_name + (len - 4), ".xml") != 0)
423 continue;
425 path = catdup(g_studios_dir, dentry->d_name);
426 if (path == NULL)
428 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "catdup() failed");
429 return false;
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));
435 free(path);
436 return false;
439 free(path);
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))
447 free(name);
448 closedir(dir);
449 return false;
452 free(name);
455 closedir(dir);
456 return true;
459 bool studio_delete(void * call_ptr, const char * studio_name)
461 char * filename;
462 char * bak_filename;
463 struct stat st;
464 bool ret;
466 ret = false;
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");
471 goto exit;
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));
479 goto free;
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));
492 ret = true;
494 free:
495 free(filename);
496 free(bak_filename);
497 exit:
498 return ret;
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;
514 char * new_name_dup;
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);
520 return;
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.");
529 return;
532 free(g_studio.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 static void ladish_studio_is_started(struct dbus_method_call * call_ptr)
585 dbus_bool_t started;
587 started = g_studio.jack_graph_proxy != NULL;
589 method_return_new_single(call_ptr, DBUS_TYPE_BOOLEAN, &started);
592 METHOD_ARGS_BEGIN(GetName, "Get studio name")
593 METHOD_ARG_DESCRIBE_OUT("studio_name", "s", "Name of studio")
594 METHOD_ARGS_END
596 METHOD_ARGS_BEGIN(Rename, "Rename studio")
597 METHOD_ARG_DESCRIBE_IN("studio_name", "s", "New name")
598 METHOD_ARGS_END
600 METHOD_ARGS_BEGIN(Save, "Save studio")
601 METHOD_ARGS_END
603 METHOD_ARGS_BEGIN(Unload, "Unload studio")
604 METHOD_ARGS_END
606 METHOD_ARGS_BEGIN(Start, "Start studio")
607 METHOD_ARGS_END
609 METHOD_ARGS_BEGIN(Stop, "Stop studio")
610 METHOD_ARGS_END
612 METHOD_ARGS_BEGIN(IsStarted, "Check whether studio is started")
613 METHOD_ARG_DESCRIBE_OUT("started", "b", "Whether studio is started")
614 METHOD_ARGS_END
616 METHODS_BEGIN
617 METHOD_DESCRIBE(GetName, ladish_get_studio_name)
618 METHOD_DESCRIBE(Rename, ladish_rename_studio)
619 METHOD_DESCRIBE(Save, ladish_save_studio)
620 METHOD_DESCRIBE(Unload, ladish_unload_studio)
621 METHOD_DESCRIBE(Start, ladish_start_studio)
622 METHOD_DESCRIBE(Stop, ladish_stop_studio)
623 METHOD_DESCRIBE(IsStarted, ladish_studio_is_started)
624 METHODS_END
626 SIGNAL_ARGS_BEGIN(StudioRenamed, "Studio name changed")
627 SIGNAL_ARG_DESCRIBE("studio_name", "s", "New studio name")
628 SIGNAL_ARGS_END
630 SIGNAL_ARGS_BEGIN(StudioStarted, "Studio started")
631 SIGNAL_ARGS_END
633 SIGNAL_ARGS_BEGIN(StudioStopped, "Studio stopped")
634 SIGNAL_ARGS_END
636 SIGNAL_ARGS_BEGIN(RoomAppeared, "Room D-Bus object appeared")
637 SIGNAL_ARG_DESCRIBE("room_path", "s", "room object path")
638 SIGNAL_ARGS_END
640 SIGNAL_ARGS_BEGIN(RoomDisappeared, "Room D-Bus object disappeared")
641 SIGNAL_ARG_DESCRIBE("room_path", "s", "room object path")
642 SIGNAL_ARGS_END
644 SIGNALS_BEGIN
645 SIGNAL_DESCRIBE(StudioRenamed)
646 SIGNAL_DESCRIBE(StudioStarted)
647 SIGNAL_DESCRIBE(StudioStopped)
648 SIGNAL_DESCRIBE(RoomAppeared)
649 SIGNAL_DESCRIBE(RoomDisappeared)
650 SIGNALS_END
652 INTERFACE_BEGIN(g_interface_studio, IFACE_STUDIO)
653 INTERFACE_DEFAULT_HANDLER
654 INTERFACE_EXPOSE_METHODS
655 INTERFACE_EXPOSE_SIGNALS
656 INTERFACE_END