daemon: improve log
[ladish.git] / daemon / studio.c
blobda5044d60ff36da2c72dec065b1cd1e5e3398a50
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 /* 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");
552 return;
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)
597 dbus_bool_t started;
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")
606 METHOD_ARGS_END
608 METHOD_ARGS_BEGIN(Rename, "Rename studio")
609 METHOD_ARG_DESCRIBE_IN("studio_name", "s", "New name")
610 METHOD_ARGS_END
612 METHOD_ARGS_BEGIN(Save, "Save studio")
613 METHOD_ARGS_END
615 METHOD_ARGS_BEGIN(Unload, "Unload studio")
616 METHOD_ARGS_END
618 METHOD_ARGS_BEGIN(Start, "Start studio")
619 METHOD_ARGS_END
621 METHOD_ARGS_BEGIN(Stop, "Stop studio")
622 METHOD_ARGS_END
624 METHOD_ARGS_BEGIN(IsStarted, "Check whether studio is started")
625 METHOD_ARG_DESCRIBE_OUT("started", "b", "Whether studio is started")
626 METHOD_ARGS_END
628 METHODS_BEGIN
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)
636 METHODS_END
638 SIGNAL_ARGS_BEGIN(StudioRenamed, "Studio name changed")
639 SIGNAL_ARG_DESCRIBE("studio_name", "s", "New studio name")
640 SIGNAL_ARGS_END
642 SIGNAL_ARGS_BEGIN(StudioStarted, "Studio started")
643 SIGNAL_ARGS_END
645 SIGNAL_ARGS_BEGIN(StudioStopped, "Studio stopped")
646 SIGNAL_ARGS_END
648 SIGNAL_ARGS_BEGIN(RoomAppeared, "Room D-Bus object appeared")
649 SIGNAL_ARG_DESCRIBE("room_path", "s", "room object path")
650 SIGNAL_ARGS_END
652 SIGNAL_ARGS_BEGIN(RoomDisappeared, "Room D-Bus object disappeared")
653 SIGNAL_ARG_DESCRIBE("room_path", "s", "room object path")
654 SIGNAL_ARGS_END
656 SIGNALS_BEGIN
657 SIGNAL_DESCRIBE(StudioRenamed)
658 SIGNAL_DESCRIBE(StudioStarted)
659 SIGNAL_DESCRIBE(StudioStopped)
660 SIGNAL_DESCRIBE(RoomAppeared)
661 SIGNAL_DESCRIBE(RoomDisappeared)
662 SIGNALS_END
664 INTERFACE_BEGIN(g_interface_studio, IFACE_STUDIO)
665 INTERFACE_DEFAULT_HANDLER
666 INTERFACE_EXPOSE_METHODS
667 INTERFACE_EXPOSE_SIGNALS
668 INTERFACE_END