root/daemon/studio.c @ c78c4c64837adcb243297a79121ddff2e683e796

Revision c78c4c64837adcb243297a79121ddff2e683e796, 31.5 KB (checked in by Nedko Arnaudov <nedko@…>, 3 years ago)

daemon: register virtualizer graph connection handlers for rooms

  • Property mode set to 100644
Line 
1/* -*- Mode: C ; c-basic-offset: 2 -*- */
2/*
3 * LADI Session Handler (ladish)
4 *
5 * Copyright (C) 2009, 2010 Nedko Arnaudov <nedko@arnaudov.name>
6 *
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 **************************************************************************
11 *
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.
16 *
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.
21 *
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.
26 */
27
28#include "common.h"
29
30#include <sys/types.h>
31#include <sys/stat.h>
32#include <unistd.h>
33#include <dirent.h>
34
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"
42#include "studio.h"
43
44#define STUDIOS_DIR "/studios/"
45char * g_studios_dir;
46
47struct studio g_studio;
48
49bool studio_name_generate(char ** name_ptr)
50{
51  time_t now;
52  char timestamp_str[26];
53  char * name;
54
55  time(&now);
56  //ctime_r(&now, timestamp_str);
57  //timestamp_str[24] = 0;
58  snprintf(timestamp_str, sizeof(timestamp_str), "%llu", (unsigned long long)now);
59
60  name = catdup("Studio ", timestamp_str);
61  if (name == NULL)
62  {
63    log_error("catdup failed to create studio name");
64    return false;
65  }
66
67  *name_ptr = name;
68  return true;
69}
70
71bool
72studio_publish(void)
73{
74  dbus_object_path object;
75
76  ASSERT(g_studio.name != NULL);
77
78  object = dbus_object_path_new(
79    STUDIO_OBJECT_PATH,
80    &g_interface_studio, &g_studio,
81    &g_interface_patchbay, ladish_graph_get_dbus_context(g_studio.studio_graph),
82    &g_iface_graph_dict, g_studio.studio_graph,
83    &g_iface_app_supervisor, g_studio.app_supervisor,
84    NULL);
85  if (object == NULL)
86  {
87    log_error("dbus_object_path_new() failed");
88    return false;
89  }
90
91  if (!dbus_object_path_register(g_dbus_connection, object))
92  {
93    log_error("object_path_register() failed");
94    dbus_object_path_destroy(g_dbus_connection, object);
95    return false;
96  }
97
98  log_info("Studio D-Bus object created. \"%s\"", g_studio.name);
99
100  g_studio.dbus_object = object;
101
102  emit_studio_appeared();
103
104  return true;
105}
106
107void emit_studio_started(void)
108{
109  dbus_signal_emit(g_dbus_connection, STUDIO_OBJECT_PATH, IFACE_STUDIO, "StudioStarted", "");
110}
111
112void emit_studio_crashed(void)
113{
114  dbus_signal_emit(g_dbus_connection, STUDIO_OBJECT_PATH, IFACE_STUDIO, "StudioCrashed", "");
115}
116
117void emit_studio_stopped(void)
118{
119  dbus_signal_emit(g_dbus_connection, STUDIO_OBJECT_PATH, IFACE_STUDIO, "StudioStopped", "");
120}
121
122static bool fill_room_info(DBusMessageIter * iter_ptr, ladish_room_handle room)
123{
124  DBusMessageIter dict_iter;
125  const char * name;
126  uuid_t template_uuid;
127  ladish_room_handle template;
128  const char * template_name;
129  const char * opath;
130
131  name = ladish_room_get_name(room);
132  opath = ladish_room_get_opath(room);
133
134  if (!ladish_room_get_template_uuid(room, template_uuid))
135  {
136    template = NULL;
137    template_name = NULL;
138  }
139  else
140  {
141    template = find_room_template_by_uuid(template_uuid);
142    if (template != NULL)
143    {
144      template_name = ladish_room_get_name(template);
145    }
146    else
147    {
148      template_name = NULL;
149    }
150  }
151
152  if (!dbus_message_iter_append_basic(iter_ptr, DBUS_TYPE_STRING, &opath))
153  {
154    log_error("dbus_message_iter_append_basic() failed.");
155    return false;
156  }
157
158  if (!dbus_message_iter_open_container(iter_ptr, DBUS_TYPE_ARRAY, "{sv}", &dict_iter))
159  {
160    log_error("dbus_message_iter_open_container() failed.");
161    return false;
162  }
163
164  if (!dbus_maybe_add_dict_entry_string(&dict_iter, "template", template_name))
165  {
166    log_error("dbus_maybe_add_dict_entry_string() failed.");
167    return false;
168  }
169
170  if (!dbus_maybe_add_dict_entry_string(&dict_iter, "name", name))
171  {
172    log_error("dbus_maybe_add_dict_entry_string() failed.");
173    return false;
174  }
175
176  if (!dbus_message_iter_close_container(iter_ptr, &dict_iter))
177  {
178    log_error("dbus_message_iter_close_container() failed.");
179    return false;
180  }
181
182  return true;
183}
184
185static void emit_room_appeared(ladish_room_handle room)
186{
187  DBusMessage * message_ptr;
188  DBusMessageIter iter;
189
190  message_ptr = dbus_message_new_signal(STUDIO_OBJECT_PATH, IFACE_STUDIO, "RoomAppeared");
191  if (message_ptr == NULL)
192  {
193    log_error("dbus_message_new_signal() failed.");
194    return;
195  }
196
197  dbus_message_iter_init_append(message_ptr, &iter);
198
199  if (fill_room_info(&iter, room))
200  {
201    dbus_signal_send(g_dbus_connection, message_ptr);
202  }
203
204  dbus_message_unref(message_ptr);
205}
206
207static void emit_room_disappeared(ladish_room_handle room)
208{
209  DBusMessage * message_ptr;
210  DBusMessageIter iter;
211
212  message_ptr = dbus_message_new_signal(STUDIO_OBJECT_PATH, IFACE_STUDIO, "RoomDisappeared");
213  if (message_ptr == NULL)
214  {
215    log_error("dbus_message_new_signal() failed.");
216    return;
217  }
218
219  dbus_message_iter_init_append(message_ptr, &iter);
220
221  if (fill_room_info(&iter, room))
222  {
223    dbus_signal_send(g_dbus_connection, message_ptr);
224  }
225
226  dbus_message_unref(message_ptr);
227}
228
229bool
230set_graph_connection_handlers(
231  void * context,
232  ladish_graph_handle graph,
233  ladish_app_supervisor_handle app_supervisor)
234{
235  ladish_virtualizer_set_graph_connection_handlers(context, graph);
236  return true;                  /* iterate all graphs */
237}
238
239void on_event_jack_started(void)
240{
241  if (!studio_fetch_jack_settings())
242  {
243    log_error("studio_fetch_jack_settings() failed.");
244
245    return;
246  }
247
248  log_info("jack conf successfully retrieved");
249  g_studio.jack_conf_valid = true;
250
251  if (!graph_proxy_create(JACKDBUS_SERVICE_NAME, JACKDBUS_OBJECT_PATH, false, &g_studio.jack_graph_proxy))
252  {
253    log_error("graph_proxy_create() failed for jackdbus");
254  }
255  else
256  {
257    if (!ladish_virtualizer_create(g_studio.jack_graph_proxy, g_studio.jack_graph, &g_studio.virtualizer))
258    {
259      log_error("ladish_virtualizer_create() failed.");
260    }
261    else
262    {
263      studio_iterate_virtual_graphs(g_studio.virtualizer, set_graph_connection_handlers);
264    }
265
266    if (!graph_proxy_activate(g_studio.jack_graph_proxy))
267    {
268      log_error("graph_proxy_activate() failed.");
269    }
270  }
271
272  ladish_app_supervisor_autorun(g_studio.app_supervisor);
273
274  emit_studio_started();
275}
276
277static void on_jack_stopped_internal(void)
278{
279  if (g_studio.virtualizer)
280  {
281    ladish_virtualizer_destroy(g_studio.virtualizer);
282    g_studio.virtualizer = NULL;
283  }
284
285  if (g_studio.jack_graph_proxy)
286  {
287    graph_proxy_destroy(g_studio.jack_graph_proxy);
288    g_studio.jack_graph_proxy = NULL;
289  }
290}
291
292void on_event_jack_stopped(void)
293{
294    emit_studio_stopped();
295    on_jack_stopped_internal();
296}
297
298void handle_unexpected_jack_server_stop(void)
299{
300    emit_studio_crashed();
301    on_jack_stopped_internal();
302
303  /* TODO: if user wants, restart jack server and reconnect all jack apps to it */
304}
305
306void studio_run(void)
307{
308  bool state;
309
310  ladish_cqueue_run(&g_studio.cmd_queue);
311  if (g_quit)
312  { /* if quit is requested, don't bother to process external events */
313    return;
314  }
315
316  if (ladish_environment_consume_change(&g_studio.env_store, ladish_environment_jack_server_started, &state))
317  {
318    ladish_cqueue_clear(&g_studio.cmd_queue);
319
320    if (state)
321    {
322      ladish_environment_ignore(&g_studio.env_store, ladish_environment_jack_server_present);
323
324      /* Automatic studio creation on JACK server start */
325      if (g_studio.dbus_object == NULL)
326      {
327        ASSERT(g_studio.name == NULL);
328        if (!studio_name_generate(&g_studio.name))
329        {
330          log_error("studio_name_generate() failed.");
331          return;
332        }
333
334        g_studio.automatic = true;
335
336        studio_publish();
337      }
338
339      on_event_jack_started();
340    }
341    else
342    {
343      /* JACK stopped but this was not expected. When expected.
344       * the change will be consumed by the run method of the studio stop command */
345
346      if (g_studio.automatic)
347      {
348        log_info("Unloading automatic studio.");
349        ladish_command_unload_studio(NULL, &g_studio.cmd_queue);
350
351        on_event_jack_stopped();
352        return;
353      }
354
355      log_error("JACK stopped unexpectedly.");
356      log_error("Save your work, then unload and reload the studio.");
357      handle_unexpected_jack_server_stop();
358    }
359  }
360
361  if (ladish_environment_consume_change(&g_studio.env_store, ladish_environment_jack_server_present, &state))
362  {
363    if (g_studio.jack_graph_proxy != NULL)
364    {
365      ladish_cqueue_clear(&g_studio.cmd_queue);
366
367      /* jack was started, this probably means that jackdbus has crashed */
368      log_error("JACK disappeared unexpectedly. Maybe it crashed.");
369      log_error("Save your work, then unload and reload the studio.");
370      ladish_environment_reset_stealth(&g_studio.env_store, ladish_environment_jack_server_started);
371
372      ladish_graph_clear(g_studio.studio_graph);
373      ladish_graph_clear(g_studio.jack_graph);
374
375      handle_unexpected_jack_server_stop();
376    }
377  }
378
379  ladish_environment_assert_consumed(&g_studio.env_store);
380}
381
382static void on_jack_server_started(void)
383{
384  log_info("JACK server start detected.");
385  ladish_environment_set(&g_studio.env_store, ladish_environment_jack_server_started);
386}
387
388static void on_jack_server_stopped(void)
389{
390  log_info("JACK server stop detected.");
391  ladish_environment_reset(&g_studio.env_store, ladish_environment_jack_server_started);
392}
393
394static void on_jack_server_appeared(void)
395{
396  log_info("JACK controller appeared.");
397  ladish_environment_set(&g_studio.env_store, ladish_environment_jack_server_present);
398}
399
400static void on_jack_server_disappeared(void)
401{
402  log_info("JACK controller disappeared.");
403  ladish_environment_reset(&g_studio.env_store, ladish_environment_jack_server_present);
404}
405
406void ladish_on_app_renamed(void * context, const char * old_name, const char * new_app_name)
407{
408  ladish_client_handle client;
409
410  client = ladish_graph_find_client_by_name(g_studio.jack_graph, old_name);
411  if (client != NULL)
412  {
413    ladish_graph_rename_client(g_studio.jack_graph, client, new_app_name);
414  }
415
416  client = ladish_graph_find_client_by_name(context, old_name);
417  if (client != NULL)
418  {
419    ladish_graph_rename_client(context, client, new_app_name);
420  }
421}
422
423bool studio_init(void)
424{
425  log_info("studio object construct");
426
427  g_studios_dir = catdup(g_base_dir, STUDIOS_DIR);
428  if (g_studios_dir == NULL)
429  {
430    log_error("catdup failed for '%s' and '%s'", g_base_dir, STUDIOS_DIR);
431    goto fail;
432  }
433
434  if (!ensure_dir_exist(g_studios_dir, 0700))
435  {
436    goto free_studios_dir;
437  }
438
439  INIT_LIST_HEAD(&g_studio.all_connections);
440  INIT_LIST_HEAD(&g_studio.all_ports);
441  INIT_LIST_HEAD(&g_studio.all_clients);
442  INIT_LIST_HEAD(&g_studio.jack_connections);
443  INIT_LIST_HEAD(&g_studio.jack_ports);
444  INIT_LIST_HEAD(&g_studio.jack_clients);
445  INIT_LIST_HEAD(&g_studio.rooms);
446  INIT_LIST_HEAD(&g_studio.clients);
447  INIT_LIST_HEAD(&g_studio.ports);
448
449  INIT_LIST_HEAD(&g_studio.jack_conf);
450  INIT_LIST_HEAD(&g_studio.jack_params);
451
452  g_studio.dbus_object = NULL;
453  g_studio.name = NULL;
454  g_studio.filename = NULL;
455
456  g_studio.room_count = 0;
457
458  if (!ladish_graph_create(&g_studio.jack_graph, NULL))
459  {
460    log_error("ladish_graph_create() failed to create jack graph object.");
461    goto free_studios_dir;
462  }
463
464  if (!ladish_graph_create(&g_studio.studio_graph, STUDIO_OBJECT_PATH))
465  {
466    log_error("ladish_graph_create() failed to create studio graph object.");
467    goto jack_graph_destroy;
468  }
469
470  if (!ladish_app_supervisor_create(&g_studio.app_supervisor, STUDIO_OBJECT_PATH, "studio", g_studio.studio_graph, ladish_on_app_renamed))
471  {
472    log_error("ladish_app_supervisor_create() failed.");
473    goto studio_graph_destroy;
474  }
475
476  ladish_cqueue_init(&g_studio.cmd_queue);
477  ladish_environment_init(&g_studio.env_store);
478
479  if (!jack_proxy_init(
480        on_jack_server_started,
481        on_jack_server_stopped,
482        on_jack_server_appeared,
483        on_jack_server_disappeared))
484  {
485    log_error("jack_proxy_init() failed.");
486    goto app_supervisor_destroy;
487  }
488
489  return true;
490
491app_supervisor_destroy:
492  ladish_app_supervisor_destroy(g_studio.app_supervisor);
493studio_graph_destroy:
494  ladish_graph_destroy(g_studio.studio_graph);
495jack_graph_destroy:
496  ladish_graph_destroy(g_studio.jack_graph);
497free_studios_dir:
498  free(g_studios_dir);
499fail:
500  return false;
501}
502
503void studio_uninit(void)
504{
505  log_info("studio_uninit()");
506
507  jack_proxy_uninit();
508
509  ladish_cqueue_clear(&g_studio.cmd_queue);
510
511  ladish_graph_destroy(g_studio.studio_graph);
512  ladish_graph_destroy(g_studio.jack_graph);
513
514  free(g_studios_dir);
515
516  log_info("studio object destroy");
517}
518
519struct on_child_exit_context
520{
521  pid_t pid;
522  bool found;
523};
524
525#define child_exit_context_ptr ((struct on_child_exit_context *)context)
526
527static
528bool
529studio_on_child_exit_callback(
530  void * context,
531  ladish_graph_handle graph,
532  ladish_app_supervisor_handle app_supervisor)
533{
534  child_exit_context_ptr->found = ladish_app_supervisor_child_exit(app_supervisor, child_exit_context_ptr->pid);
535  /* if child is found, return false - it will cause iteration to stop */
536  /* if child is not found, return true - it will cause next supervisor to be checked */
537  return !child_exit_context_ptr->found;
538}
539
540#undef child_exit_context_ptr
541
542void studio_on_child_exit(pid_t pid)
543{
544  struct on_child_exit_context context;
545
546  context.pid = pid;
547  context.found = false;
548
549  studio_iterate_virtual_graphs(&context, studio_on_child_exit_callback);
550
551  if (!context.found)
552  {
553    log_error("unknown child exit detected. pid is %llu", (unsigned long long)pid);
554  }
555}
556
557bool studio_is_loaded(void)
558{
559  return g_studio.dbus_object != NULL;
560}
561
562bool studio_is_started(void)
563{
564  return ladish_environment_get(&g_studio.env_store, ladish_environment_jack_server_started);
565}
566
567bool studio_compose_filename(const char * name, char ** filename_ptr_ptr, char ** backup_filename_ptr_ptr)
568{
569  size_t len_dir;
570  char * p;
571  const char * src;
572  char * filename_ptr;
573  char * backup_filename_ptr = NULL;
574
575  len_dir = strlen(g_studios_dir);
576
577  filename_ptr = malloc(len_dir + 1 + strlen(name) * 3 + 4 + 1);
578  if (filename_ptr == NULL)
579  {
580    log_error("malloc failed to allocate memory for studio file path");
581    return false;
582  }
583
584  if (backup_filename_ptr_ptr != NULL)
585  {
586    backup_filename_ptr = malloc(len_dir + 1 + strlen(name) * 3 + 4 + 4 + 1);
587    if (backup_filename_ptr == NULL)
588    {
589      log_error("malloc failed to allocate memory for studio backup file path");
590      free(filename_ptr);
591      return false;
592    }
593  }
594
595  p = filename_ptr;
596  memcpy(p, g_studios_dir, len_dir);
597  p += len_dir;
598
599  *p++ = '/';
600
601  src = name;
602  escape(&src, &p);
603  strcpy(p, ".xml");
604
605  *filename_ptr_ptr = filename_ptr;
606
607  if (backup_filename_ptr_ptr != NULL)
608  {
609    strcpy(backup_filename_ptr, filename_ptr);
610    strcat(backup_filename_ptr, ".bak");
611    *backup_filename_ptr_ptr = backup_filename_ptr;
612  }
613
614  return true;
615}
616
617bool studios_iterate(void * call_ptr, void * context, bool (* callback)(void * call_ptr, void * context, const char * studio, uint32_t modtime))
618{
619  DIR * dir;
620  struct dirent * dentry;
621  size_t len;
622  struct stat st;
623  char * path;
624  char * name;
625
626  dir = opendir(g_studios_dir);
627  if (dir == NULL)
628  {
629    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Cannot open directory '%s': %d (%s)", g_studios_dir, errno, strerror(errno));
630    return false;
631  }
632
633  while ((dentry = readdir(dir)) != NULL)
634  {
635    len = strlen(dentry->d_name);
636    if (len <= 4 || strcmp(dentry->d_name + (len - 4), ".xml") != 0)
637      continue;
638
639    path = catdup(g_studios_dir, dentry->d_name);
640    if (path == NULL)
641    {
642      lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "catdup() failed");
643      return false;
644    }
645
646    if (stat(path, &st) != 0)
647    {
648      lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "failed to stat '%s': %d (%s)", path, errno, strerror(errno));
649      free(path);
650      return false;
651    }
652
653    free(path);
654
655    if (!S_ISREG(st.st_mode))
656    {
657      //log_info("Ignoring direntry that is not regular file. Mode is %07o", st.st_mode);
658      continue;
659    }
660
661    name = malloc(len - 4 + 1);
662    if (name == NULL)
663    {
664      log_error("malloc() failed.");
665      closedir(dir);
666      return false;
667    }
668
669    name[unescape(dentry->d_name, len - 4, name)] = 0;
670    //log_info("name = '%s'", name);
671
672    if (!callback(call_ptr, context, name, st.st_mtime))
673    {
674      free(name);
675      closedir(dir);
676      return false;
677    }
678
679    free(name);
680  }
681
682  closedir(dir);
683  return true;
684}
685
686bool studio_delete(void * call_ptr, const char * studio_name)
687{
688  char * filename;
689  char * bak_filename;
690  struct stat st;
691  bool ret;
692
693  ret = false;
694
695  if (!studio_compose_filename(studio_name, &filename, &bak_filename))
696  {
697    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "failed to compose studio filename");
698    goto exit;
699  }
700
701  log_info("Deleting studio ('%s')", filename);
702
703  if (unlink(filename) != 0)
704  {
705    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "unlink(%s) failed: %d (%s)", filename, errno, strerror(errno));
706    goto free;
707  }
708
709  /* try to delete the backup file */
710  if (stat(bak_filename, &st) == 0)
711  {
712    if (unlink(bak_filename) != 0)
713    {
714      /* failing to delete backup file will not case delete command failure */
715      log_error("unlink(%s) failed: %d (%s)", bak_filename, errno, strerror(errno));
716    }
717  }
718
719  ret = true;
720
721free:
722  free(filename);
723  free(bak_filename);
724exit:
725  return ret;
726}
727
728bool
729studio_iterate_virtual_graphs(
730  void * context,
731  bool (* callback)(
732    void * context,
733    ladish_graph_handle graph,
734    ladish_app_supervisor_handle app_supervisor))
735{
736  struct list_head * node_ptr;
737  ladish_room_handle room;
738  ladish_app_supervisor_handle room_app_supervisor;
739  ladish_graph_handle room_graph;
740
741  if (!callback(context, g_studio.studio_graph, g_studio.app_supervisor))
742  {
743    return false;
744  }
745
746  list_for_each(node_ptr, &g_studio.rooms)
747  {
748    room = ladish_room_from_list_node(node_ptr);
749    room_app_supervisor = ladish_room_get_app_supervisor(room);
750    ASSERT(room_app_supervisor != NULL);
751    room_graph = ladish_room_get_graph(room);
752
753    if (!callback(context, room_graph, room_app_supervisor))
754    {
755      return false;
756    }
757  }
758
759  return true;
760}
761
762static bool studio_stop_app_supervisor(void * context, ladish_graph_handle graph, ladish_app_supervisor_handle app_supervisor)
763{
764  ladish_app_supervisor_stop(app_supervisor);
765  return true;                  /* iterate all supervisors */
766}
767
768void studio_stop_app_supervisors(void)
769{
770  studio_iterate_virtual_graphs(NULL, studio_stop_app_supervisor);
771}
772
773void emit_studio_renamed()
774{
775  dbus_signal_emit(g_dbus_connection, STUDIO_OBJECT_PATH, IFACE_STUDIO, "StudioRenamed", "s", &g_studio.name);
776}
777
778static void ladish_get_studio_name(struct dbus_method_call * call_ptr)
779{
780  method_return_new_single(call_ptr, DBUS_TYPE_STRING, &g_studio.name);
781}
782
783static void ladish_rename_studio(struct dbus_method_call * call_ptr)
784{
785  const char * new_name;
786  char * new_name_dup;
787 
788  if (!dbus_message_get_args(call_ptr->message, &g_dbus_error, DBUS_TYPE_STRING, &new_name, DBUS_TYPE_INVALID))
789  {
790    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s",  call_ptr->method_name, g_dbus_error.message);
791    dbus_error_free(&g_dbus_error);
792    return;
793  }
794
795  log_info("Rename studio request (%s)", new_name);
796
797  new_name_dup = strdup(new_name);
798  if (new_name_dup == NULL)
799  {
800    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "strdup() failed to allocate new name.");
801    return;
802  }
803
804  free(g_studio.name);
805  g_studio.name = new_name_dup;
806
807  method_return_new_void(call_ptr);
808  emit_studio_renamed();
809}
810
811static bool ladish_save_studio_internal(struct dbus_method_call * call_ptr, const char * new_studio_name)
812{
813  /* FIXME: this is wrong place to do such check because state before
814     command execution needs to be checked and not state before
815     command is submited, but doing it here will show error to
816     user. Once notification mechanism is implemented, the
817     studio_is_started() check in save command run menthod
818     will send a notification and this check must be removed. */
819  if (!studio_is_started())
820  {
821    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Cannot save not-started studio");
822    return false;
823  }
824
825  return ladish_command_save_studio(call_ptr, &g_studio.cmd_queue, new_studio_name);
826}
827
828static void ladish_save_studio(struct dbus_method_call * call_ptr)
829{
830  log_info("Save studio request");
831
832  if (ladish_save_studio_internal(call_ptr, g_studio.name))
833  {
834    method_return_new_void(call_ptr);
835  }
836}
837
838static void ladish_save_as_studio(struct dbus_method_call * call_ptr)
839{
840  const char * new_name;
841
842  log_info("SaveAs studio request");
843
844  if (!dbus_message_get_args(call_ptr->message, &g_dbus_error, DBUS_TYPE_STRING, &new_name, DBUS_TYPE_INVALID))
845  {
846    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s",  call_ptr->method_name, g_dbus_error.message);
847    dbus_error_free(&g_dbus_error);
848    return;
849  }
850
851  if (ladish_save_studio_internal(call_ptr, new_name))
852  {
853    method_return_new_void(call_ptr);
854  }
855}
856
857static void ladish_unload_studio(struct dbus_method_call * call_ptr)
858{
859  log_info("Unload studio request");
860
861  if (ladish_command_unload_studio(call_ptr, &g_studio.cmd_queue))
862  {
863    method_return_new_void(call_ptr);
864  }
865}
866
867static void ladish_stop_studio(struct dbus_method_call * call_ptr)
868{
869  log_info("Stop studio request");
870
871  g_studio.automatic = false;   /* even if it was automatic, it is not anymore because user knows about it */
872
873  if (ladish_command_stop_studio(call_ptr, &g_studio.cmd_queue))
874  {
875    method_return_new_void(call_ptr);
876  }
877}
878
879static void ladish_start_studio(struct dbus_method_call * call_ptr)
880{
881  log_info("Start studio request");
882
883  g_studio.automatic = false;   /* even if it was automatic, it is not anymore because user knows about it */
884
885  if (ladish_command_start_studio(call_ptr, &g_studio.cmd_queue))
886  {
887    method_return_new_void(call_ptr);
888  }
889}
890
891static void ladish_studio_is_started(struct dbus_method_call * call_ptr)
892{
893  dbus_bool_t started;
894
895  started = g_studio.jack_graph_proxy != NULL;
896
897  method_return_new_single(call_ptr, DBUS_TYPE_BOOLEAN, &started);
898}
899
900static
901bool
902add_room_ports(
903  void * context,
904  ladish_port_handle port_handle,
905  const char * port_name,
906  uint32_t port_type,
907  uint32_t port_flags)
908{
909  //log_info("Studio room port \"%s\"", port_name);
910
911  if (JACKDBUS_PORT_IS_INPUT(port_flags))
912  {
913    JACKDBUS_PORT_CLEAR_INPUT(port_flags);
914    JACKDBUS_PORT_SET_OUTPUT(port_flags);
915  }
916  else if (JACKDBUS_PORT_IS_OUTPUT(port_flags))
917  {
918    JACKDBUS_PORT_CLEAR_OUTPUT(port_flags);
919    JACKDBUS_PORT_SET_INPUT(port_flags);
920  }
921  else
922  {
923    log_error("room link port with bad flags %"PRIu32, port_flags);
924    return false;
925  }
926
927  return ladish_graph_add_port(g_studio.studio_graph, context, port_handle, port_name, port_type, port_flags, false);
928}
929
930static void ladish_studio_create_room(struct dbus_method_call * call_ptr)
931{
932  const char * room_name;
933  const char * template_name;
934  ladish_room_handle room;
935  char room_dbus_name[1024];
936  ladish_client_handle room_client;
937  uuid_t room_uuid;
938
939  dbus_error_init(&g_dbus_error);
940
941  if (!dbus_message_get_args(call_ptr->message, &g_dbus_error, DBUS_TYPE_STRING, &room_name, DBUS_TYPE_STRING, &template_name, DBUS_TYPE_INVALID))
942  {
943    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s",  call_ptr->method_name, g_dbus_error.message);
944    dbus_error_free(&g_dbus_error);
945    goto fail;
946  }
947
948  log_info("Request to create new studio room \"%s\" from template \"%s\".", room_name, template_name);
949
950  room = find_room_template_by_name(template_name);
951  if (room == NULL)
952  {
953    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Unknown room template \"%s\"",  template_name);
954    goto fail;
955  }
956
957  g_studio.room_count++;
958
959  sprintf(room_dbus_name, DBUS_BASE_PATH "/Room%u", g_studio.room_count);
960
961  if (!ladish_room_create(NULL, room_name, room, room_dbus_name, &room))
962  {
963    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "ladish_room_create() failed.");
964    goto fail_decrement_room_count;
965  }
966
967  if (g_studio.virtualizer != NULL)
968  {
969    ladish_virtualizer_set_graph_connection_handlers(g_studio.virtualizer, ladish_room_get_graph(room));
970  }
971
972  ladish_room_get_uuid(room, room_uuid);
973
974  if (!ladish_client_create(room_uuid, &room_client))
975  {
976    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "ladish_client_create() failed.");
977    goto fail_destroy_room;
978  }
979
980  if (!ladish_graph_add_client(g_studio.studio_graph, room_client, room_name, false))
981  {
982    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "ladish_graph_add_client() failed to add room client to studio graph.");
983    goto fail_destroy_room_client;
984  }
985
986  if (!ladish_room_iterate_link_ports(room, room_client, add_room_ports))
987  {
988    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Creation of studio room link ports failed.");
989    goto fail_remove_room_client;
990  }
991
992  list_add_tail(ladish_room_get_list_node(room), &g_studio.rooms);
993
994  emit_room_appeared(room);
995
996  method_return_new_void(call_ptr);
997  return;
998
999fail_remove_room_client:
1000  ladish_graph_remove_client(g_studio.studio_graph, room_client);
1001fail_destroy_room_client:
1002  ladish_client_destroy(room_client);
1003fail_destroy_room:
1004  ladish_room_destroy(room);
1005fail_decrement_room_count:
1006  g_studio.room_count--;
1007fail:
1008  return;
1009}
1010
1011static void ladish_studio_get_room_list(struct dbus_method_call * call_ptr)
1012{
1013  DBusMessageIter iter, array_iter;
1014  DBusMessageIter struct_iter;
1015  struct list_head * node_ptr;
1016  ladish_room_handle room;
1017
1018  call_ptr->reply = dbus_message_new_method_return(call_ptr->message);
1019  if (call_ptr->reply == NULL)
1020  {
1021    goto fail;
1022  }
1023
1024  dbus_message_iter_init_append(call_ptr->reply, &iter);
1025
1026  if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sa{sv})", &array_iter))
1027  {
1028    goto fail_unref;
1029  }
1030
1031  list_for_each(node_ptr, &g_studio.rooms)
1032  {
1033    room = ladish_room_from_list_node(node_ptr);
1034
1035    if (!dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter))
1036      goto fail_unref;
1037
1038    if (!fill_room_info(&struct_iter, room))
1039      goto fail_unref;
1040
1041    if (!dbus_message_iter_close_container(&array_iter, &struct_iter))
1042      goto fail_unref;
1043  }
1044
1045  if (!dbus_message_iter_close_container(&iter, &array_iter))
1046  {
1047    goto fail_unref;
1048  }
1049
1050  return;
1051
1052fail_unref:
1053  dbus_message_unref(call_ptr->reply);
1054  call_ptr->reply = NULL;
1055
1056fail:
1057  log_error("Ran out of memory trying to construct method return");
1058}
1059
1060static void ladish_studio_delete_room(struct dbus_method_call * call_ptr)
1061{
1062  const char * name;
1063  struct list_head * node_ptr;
1064  ladish_room_handle room;
1065  ladish_client_handle room_client;
1066
1067  dbus_error_init(&g_dbus_error);
1068
1069  if (!dbus_message_get_args(call_ptr->message, &g_dbus_error, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID))
1070  {
1071    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s",  call_ptr->method_name, g_dbus_error.message);
1072    dbus_error_free(&g_dbus_error);
1073    return;
1074  }
1075
1076  log_info("Delete studio room request (%s)", name);
1077
1078  list_for_each(node_ptr, &g_studio.rooms)
1079  {
1080    room = ladish_room_from_list_node(node_ptr);
1081    if (strcmp(ladish_room_get_name(room), name) == 0)
1082    {
1083      list_del(node_ptr);
1084      g_studio.room_count--;
1085      emit_room_disappeared(room);
1086
1087      room_client = ladish_graph_find_client_by_name(g_studio.studio_graph, ladish_room_get_name(room));
1088      ASSERT(room_client != NULL);
1089      ladish_graph_remove_client(g_studio.studio_graph, room_client);
1090      ladish_client_destroy(room_client);
1091
1092      ladish_room_destroy(room);
1093      method_return_new_void(call_ptr);
1094      return;
1095    }
1096  }
1097
1098  lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": Cannot find room with name \"%s\"",  call_ptr->method_name, name);
1099  return;
1100}
1101
1102void studio_remove_all_rooms(void)
1103{
1104  struct list_head * node_ptr;
1105  ladish_room_handle room;
1106
1107  while (!list_empty(&g_studio.rooms))
1108  {
1109    node_ptr = g_studio.rooms.next;
1110    list_del(node_ptr);
1111    room = ladish_room_from_list_node(node_ptr);
1112    ASSERT(g_studio.room_count > 0);
1113    g_studio.room_count--;
1114    emit_room_disappeared(room);
1115    ladish_room_destroy(room);
1116  }
1117
1118  ASSERT(g_studio.room_count == 0);
1119}
1120
1121METHOD_ARGS_BEGIN(GetName, "Get studio name")
1122  METHOD_ARG_DESCRIBE_OUT("studio_name", "s", "Name of studio")
1123METHOD_ARGS_END
1124
1125METHOD_ARGS_BEGIN(Rename, "Rename studio")
1126  METHOD_ARG_DESCRIBE_IN("studio_name", "s", "New name")
1127METHOD_ARGS_END
1128
1129METHOD_ARGS_BEGIN(Save, "Save studio")
1130METHOD_ARGS_END
1131
1132METHOD_ARGS_BEGIN(SaveAs, "SaveAs studio")
1133  METHOD_ARG_DESCRIBE_IN("studio_name", "s", "New name")
1134METHOD_ARGS_END
1135
1136METHOD_ARGS_BEGIN(Unload, "Unload studio")
1137METHOD_ARGS_END
1138
1139METHOD_ARGS_BEGIN(Start, "Start studio")
1140METHOD_ARGS_END
1141
1142METHOD_ARGS_BEGIN(Stop, "Stop studio")
1143METHOD_ARGS_END
1144
1145METHOD_ARGS_BEGIN(IsStarted, "Check whether studio is started")
1146  METHOD_ARG_DESCRIBE_OUT("started", "b", "Whether studio is started")
1147METHOD_ARGS_END
1148
1149METHOD_ARGS_BEGIN(CreateRoom, "Create new studio room")
1150  METHOD_ARG_DESCRIBE_IN("room_name", "s", "Studio room name")
1151  METHOD_ARG_DESCRIBE_IN("room_template_name", "s", "Room template name")
1152METHOD_ARGS_END
1153
1154METHOD_ARGS_BEGIN(GetRoomList, "Get list of rooms in this studio")
1155  METHOD_ARG_DESCRIBE_OUT("room_list", "a(sa{sv})", "List of studio rooms: opaths and properties")
1156METHOD_ARGS_END
1157
1158METHOD_ARGS_BEGIN(DeleteRoom, "Delete studio room")
1159  METHOD_ARG_DESCRIBE_IN("room_name", "s", "Name of studio room to delete")
1160METHOD_ARGS_END
1161
1162METHODS_BEGIN
1163  METHOD_DESCRIBE(GetName, ladish_get_studio_name)
1164  METHOD_DESCRIBE(Rename, ladish_rename_studio)
1165  METHOD_DESCRIBE(Save, ladish_save_studio)
1166  METHOD_DESCRIBE(SaveAs, ladish_save_as_studio)
1167  METHOD_DESCRIBE(Unload, ladish_unload_studio)
1168  METHOD_DESCRIBE(Start, ladish_start_studio)
1169  METHOD_DESCRIBE(Stop, ladish_stop_studio)
1170  METHOD_DESCRIBE(IsStarted, ladish_studio_is_started)
1171  METHOD_DESCRIBE(CreateRoom, ladish_studio_create_room)
1172  METHOD_DESCRIBE(GetRoomList, ladish_studio_get_room_list)
1173  METHOD_DESCRIBE(DeleteRoom, ladish_studio_delete_room)
1174METHODS_END
1175
1176SIGNAL_ARGS_BEGIN(StudioRenamed, "Studio name changed")
1177  SIGNAL_ARG_DESCRIBE("studio_name", "s", "New studio name")
1178SIGNAL_ARGS_END
1179
1180SIGNAL_ARGS_BEGIN(StudioStarted, "Studio started")
1181SIGNAL_ARGS_END
1182
1183SIGNAL_ARGS_BEGIN(StudioCrashed, "Studio crashed")
1184SIGNAL_ARGS_END
1185
1186SIGNAL_ARGS_BEGIN(StudioStopped, "Studio stopped")
1187SIGNAL_ARGS_END
1188
1189SIGNAL_ARGS_BEGIN(RoomAppeared, "Room D-Bus object appeared")
1190  SIGNAL_ARG_DESCRIBE("opath", "s", "room object path")
1191  SIGNAL_ARG_DESCRIBE("properties", "a{sv}", "room object path and props")
1192SIGNAL_ARGS_END
1193
1194SIGNAL_ARGS_BEGIN(RoomChanged, "Room D-Bus object changed")
1195  SIGNAL_ARG_DESCRIBE("opath", "s", "room object path")
1196  SIGNAL_ARG_DESCRIBE("properties", "a{sv}", "room object path and props")
1197SIGNAL_ARGS_END
1198
1199SIGNAL_ARGS_BEGIN(RoomDisappeared, "Room D-Bus object disappeared")
1200  SIGNAL_ARG_DESCRIBE("opath", "s", "room object path")
1201  SIGNAL_ARG_DESCRIBE("properties", "a{sv}", "room object path and props")
1202SIGNAL_ARGS_END
1203
1204SIGNALS_BEGIN
1205  SIGNAL_DESCRIBE(StudioRenamed)
1206  SIGNAL_DESCRIBE(StudioStarted)
1207  SIGNAL_DESCRIBE(StudioCrashed)
1208  SIGNAL_DESCRIBE(StudioStopped)
1209  SIGNAL_DESCRIBE(RoomAppeared)
1210  SIGNAL_DESCRIBE(RoomDisappeared)
1211  SIGNAL_DESCRIBE(RoomChanged)
1212SIGNALS_END
1213
1214INTERFACE_BEGIN(g_interface_studio, IFACE_STUDIO)
1215  INTERFACE_DEFAULT_HANDLER
1216  INTERFACE_EXPOSE_METHODS
1217  INTERFACE_EXPOSE_SIGNALS
1218INTERFACE_END
Note: See TracBrowser for help on using the browser.