root/daemon/studio.c @ ef08b85792e6f41ce0f14e44c93487056f56047a

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

daemon: on jack crash, properly cleanup non virtual graph objects

  • room graphs need cleanup as well
  • graphs should not be cleared because clearing is not suitable for restart jack functionality that is planned
  • 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
306static bool hide_vgraph_non_virtual(void * context, ladish_graph_handle graph, ladish_app_supervisor_handle app_supervisor)
307{
308  ladish_graph_hide_non_virtual(graph);
309  return true;                  /* iterate all vgraphs */
310}
311
312void studio_run(void)
313{
314  bool state;
315
316  ladish_cqueue_run(&g_studio.cmd_queue);
317  if (g_quit)
318  { /* if quit is requested, don't bother to process external events */
319    return;
320  }
321
322  if (ladish_environment_consume_change(&g_studio.env_store, ladish_environment_jack_server_started, &state))
323  {
324    ladish_cqueue_clear(&g_studio.cmd_queue);
325
326    if (state)
327    {
328      ladish_environment_ignore(&g_studio.env_store, ladish_environment_jack_server_present);
329
330      /* Automatic studio creation on JACK server start */
331      if (g_studio.dbus_object == NULL)
332      {
333        ASSERT(g_studio.name == NULL);
334        if (!studio_name_generate(&g_studio.name))
335        {
336          log_error("studio_name_generate() failed.");
337          return;
338        }
339
340        g_studio.automatic = true;
341
342        studio_publish();
343      }
344
345      on_event_jack_started();
346    }
347    else
348    {
349      /* JACK stopped but this was not expected. When expected.
350       * the change will be consumed by the run method of the studio stop command */
351
352      if (g_studio.automatic)
353      {
354        log_info("Unloading automatic studio.");
355        ladish_command_unload_studio(NULL, &g_studio.cmd_queue);
356
357        on_event_jack_stopped();
358        return;
359      }
360
361      log_error("JACK stopped unexpectedly.");
362      log_error("Save your work, then unload and reload the studio.");
363      handle_unexpected_jack_server_stop();
364    }
365  }
366
367  if (ladish_environment_consume_change(&g_studio.env_store, ladish_environment_jack_server_present, &state))
368  {
369    if (g_studio.jack_graph_proxy != NULL)
370    {
371      ladish_cqueue_clear(&g_studio.cmd_queue);
372
373      /* jack was started, this probably means that jackdbus has crashed */
374      log_error("JACK disappeared unexpectedly. Maybe it crashed.");
375      log_error("Save your work, then unload and reload the studio.");
376      ladish_environment_reset_stealth(&g_studio.env_store, ladish_environment_jack_server_started);
377
378      studio_iterate_virtual_graphs(NULL, hide_vgraph_non_virtual);
379
380      handle_unexpected_jack_server_stop();
381    }
382  }
383
384  ladish_environment_assert_consumed(&g_studio.env_store);
385}
386
387static void on_jack_server_started(void)
388{
389  log_info("JACK server start detected.");
390  ladish_environment_set(&g_studio.env_store, ladish_environment_jack_server_started);
391}
392
393static void on_jack_server_stopped(void)
394{
395  log_info("JACK server stop detected.");
396  ladish_environment_reset(&g_studio.env_store, ladish_environment_jack_server_started);
397}
398
399static void on_jack_server_appeared(void)
400{
401  log_info("JACK controller appeared.");
402  ladish_environment_set(&g_studio.env_store, ladish_environment_jack_server_present);
403}
404
405static void on_jack_server_disappeared(void)
406{
407  log_info("JACK controller disappeared.");
408  ladish_environment_reset(&g_studio.env_store, ladish_environment_jack_server_present);
409}
410
411void ladish_on_app_renamed(void * context, const char * old_name, const char * new_app_name)
412{
413  ladish_client_handle client;
414
415  client = ladish_graph_find_client_by_name(g_studio.jack_graph, old_name);
416  if (client != NULL)
417  {
418    ladish_graph_rename_client(g_studio.jack_graph, client, new_app_name);
419  }
420
421  client = ladish_graph_find_client_by_name(context, old_name);
422  if (client != NULL)
423  {
424    ladish_graph_rename_client(context, client, new_app_name);
425  }
426}
427
428bool studio_init(void)
429{
430  log_info("studio object construct");
431
432  g_studios_dir = catdup(g_base_dir, STUDIOS_DIR);
433  if (g_studios_dir == NULL)
434  {
435    log_error("catdup failed for '%s' and '%s'", g_base_dir, STUDIOS_DIR);
436    goto fail;
437  }
438
439  if (!ensure_dir_exist(g_studios_dir, 0700))
440  {
441    goto free_studios_dir;
442  }
443
444  INIT_LIST_HEAD(&g_studio.all_connections);
445  INIT_LIST_HEAD(&g_studio.all_ports);
446  INIT_LIST_HEAD(&g_studio.all_clients);
447  INIT_LIST_HEAD(&g_studio.jack_connections);
448  INIT_LIST_HEAD(&g_studio.jack_ports);
449  INIT_LIST_HEAD(&g_studio.jack_clients);
450  INIT_LIST_HEAD(&g_studio.rooms);
451  INIT_LIST_HEAD(&g_studio.clients);
452  INIT_LIST_HEAD(&g_studio.ports);
453
454  INIT_LIST_HEAD(&g_studio.jack_conf);
455  INIT_LIST_HEAD(&g_studio.jack_params);
456
457  g_studio.dbus_object = NULL;
458  g_studio.name = NULL;
459  g_studio.filename = NULL;
460
461  g_studio.room_count = 0;
462
463  if (!ladish_graph_create(&g_studio.jack_graph, NULL))
464  {
465    log_error("ladish_graph_create() failed to create jack graph object.");
466    goto free_studios_dir;
467  }
468
469  if (!ladish_graph_create(&g_studio.studio_graph, STUDIO_OBJECT_PATH))
470  {
471    log_error("ladish_graph_create() failed to create studio graph object.");
472    goto jack_graph_destroy;
473  }
474
475  if (!ladish_app_supervisor_create(&g_studio.app_supervisor, STUDIO_OBJECT_PATH, "studio", g_studio.studio_graph, ladish_on_app_renamed))
476  {
477    log_error("ladish_app_supervisor_create() failed.");
478    goto studio_graph_destroy;
479  }
480
481  ladish_cqueue_init(&g_studio.cmd_queue);
482  ladish_environment_init(&g_studio.env_store);
483
484  if (!jack_proxy_init(
485        on_jack_server_started,
486        on_jack_server_stopped,
487        on_jack_server_appeared,
488        on_jack_server_disappeared))
489  {
490    log_error("jack_proxy_init() failed.");
491    goto app_supervisor_destroy;
492  }
493
494  return true;
495
496app_supervisor_destroy:
497  ladish_app_supervisor_destroy(g_studio.app_supervisor);
498studio_graph_destroy:
499  ladish_graph_destroy(g_studio.studio_graph);
500jack_graph_destroy:
501  ladish_graph_destroy(g_studio.jack_graph);
502free_studios_dir:
503  free(g_studios_dir);
504fail:
505  return false;
506}
507
508void studio_uninit(void)
509{
510  log_info("studio_uninit()");
511
512  jack_proxy_uninit();
513
514  ladish_cqueue_clear(&g_studio.cmd_queue);
515
516  ladish_graph_destroy(g_studio.studio_graph);
517  ladish_graph_destroy(g_studio.jack_graph);
518
519  free(g_studios_dir);
520
521  log_info("studio object destroy");
522}
523
524struct on_child_exit_context
525{
526  pid_t pid;
527  bool found;
528};
529
530#define child_exit_context_ptr ((struct on_child_exit_context *)context)
531
532static
533bool
534studio_on_child_exit_callback(
535  void * context,
536  ladish_graph_handle graph,
537  ladish_app_supervisor_handle app_supervisor)
538{
539  child_exit_context_ptr->found = ladish_app_supervisor_child_exit(app_supervisor, child_exit_context_ptr->pid);
540  /* if child is found, return false - it will cause iteration to stop */
541  /* if child is not found, return true - it will cause next supervisor to be checked */
542  return !child_exit_context_ptr->found;
543}
544
545#undef child_exit_context_ptr
546
547void studio_on_child_exit(pid_t pid)
548{
549  struct on_child_exit_context context;
550
551  context.pid = pid;
552  context.found = false;
553
554  studio_iterate_virtual_graphs(&context, studio_on_child_exit_callback);
555
556  if (!context.found)
557  {
558    log_error("unknown child exit detected. pid is %llu", (unsigned long long)pid);
559  }
560}
561
562bool studio_is_loaded(void)
563{
564  return g_studio.dbus_object != NULL;
565}
566
567bool studio_is_started(void)
568{
569  return ladish_environment_get(&g_studio.env_store, ladish_environment_jack_server_started);
570}
571
572bool studio_compose_filename(const char * name, char ** filename_ptr_ptr, char ** backup_filename_ptr_ptr)
573{
574  size_t len_dir;
575  char * p;
576  const char * src;
577  char * filename_ptr;
578  char * backup_filename_ptr = NULL;
579
580  len_dir = strlen(g_studios_dir);
581
582  filename_ptr = malloc(len_dir + 1 + strlen(name) * 3 + 4 + 1);
583  if (filename_ptr == NULL)
584  {
585    log_error("malloc failed to allocate memory for studio file path");
586    return false;
587  }
588
589  if (backup_filename_ptr_ptr != NULL)
590  {
591    backup_filename_ptr = malloc(len_dir + 1 + strlen(name) * 3 + 4 + 4 + 1);
592    if (backup_filename_ptr == NULL)
593    {
594      log_error("malloc failed to allocate memory for studio backup file path");
595      free(filename_ptr);
596      return false;
597    }
598  }
599
600  p = filename_ptr;
601  memcpy(p, g_studios_dir, len_dir);
602  p += len_dir;
603
604  *p++ = '/';
605
606  src = name;
607  escape(&src, &p);
608  strcpy(p, ".xml");
609
610  *filename_ptr_ptr = filename_ptr;
611
612  if (backup_filename_ptr_ptr != NULL)
613  {
614    strcpy(backup_filename_ptr, filename_ptr);
615    strcat(backup_filename_ptr, ".bak");
616    *backup_filename_ptr_ptr = backup_filename_ptr;
617  }
618
619  return true;
620}
621
622bool studios_iterate(void * call_ptr, void * context, bool (* callback)(void * call_ptr, void * context, const char * studio, uint32_t modtime))
623{
624  DIR * dir;
625  struct dirent * dentry;
626  size_t len;
627  struct stat st;
628  char * path;
629  char * name;
630
631  dir = opendir(g_studios_dir);
632  if (dir == NULL)
633  {
634    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Cannot open directory '%s': %d (%s)", g_studios_dir, errno, strerror(errno));
635    return false;
636  }
637
638  while ((dentry = readdir(dir)) != NULL)
639  {
640    len = strlen(dentry->d_name);
641    if (len <= 4 || strcmp(dentry->d_name + (len - 4), ".xml") != 0)
642      continue;
643
644    path = catdup(g_studios_dir, dentry->d_name);
645    if (path == NULL)
646    {
647      lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "catdup() failed");
648      return false;
649    }
650
651    if (stat(path, &st) != 0)
652    {
653      lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "failed to stat '%s': %d (%s)", path, errno, strerror(errno));
654      free(path);
655      return false;
656    }
657
658    free(path);
659
660    if (!S_ISREG(st.st_mode))
661    {
662      //log_info("Ignoring direntry that is not regular file. Mode is %07o", st.st_mode);
663      continue;
664    }
665
666    name = malloc(len - 4 + 1);
667    if (name == NULL)
668    {
669      log_error("malloc() failed.");
670      closedir(dir);
671      return false;
672    }
673
674    name[unescape(dentry->d_name, len - 4, name)] = 0;
675    //log_info("name = '%s'", name);
676
677    if (!callback(call_ptr, context, name, st.st_mtime))
678    {
679      free(name);
680      closedir(dir);
681      return false;
682    }
683
684    free(name);
685  }
686
687  closedir(dir);
688  return true;
689}
690
691bool studio_delete(void * call_ptr, const char * studio_name)
692{
693  char * filename;
694  char * bak_filename;
695  struct stat st;
696  bool ret;
697
698  ret = false;
699
700  if (!studio_compose_filename(studio_name, &filename, &bak_filename))
701  {
702    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "failed to compose studio filename");
703    goto exit;
704  }
705
706  log_info("Deleting studio ('%s')", filename);
707
708  if (unlink(filename) != 0)
709  {
710    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "unlink(%s) failed: %d (%s)", filename, errno, strerror(errno));
711    goto free;
712  }
713
714  /* try to delete the backup file */
715  if (stat(bak_filename, &st) == 0)
716  {
717    if (unlink(bak_filename) != 0)
718    {
719      /* failing to delete backup file will not case delete command failure */
720      log_error("unlink(%s) failed: %d (%s)", bak_filename, errno, strerror(errno));
721    }
722  }
723
724  ret = true;
725
726free:
727  free(filename);
728  free(bak_filename);
729exit:
730  return ret;
731}
732
733bool
734studio_iterate_virtual_graphs(
735  void * context,
736  bool (* callback)(
737    void * context,
738    ladish_graph_handle graph,
739    ladish_app_supervisor_handle app_supervisor))
740{
741  struct list_head * node_ptr;
742  ladish_room_handle room;
743  ladish_app_supervisor_handle room_app_supervisor;
744  ladish_graph_handle room_graph;
745
746  if (!callback(context, g_studio.studio_graph, g_studio.app_supervisor))
747  {
748    return false;
749  }
750
751  list_for_each(node_ptr, &g_studio.rooms)
752  {
753    room = ladish_room_from_list_node(node_ptr);
754    room_app_supervisor = ladish_room_get_app_supervisor(room);
755    ASSERT(room_app_supervisor != NULL);
756    room_graph = ladish_room_get_graph(room);
757
758    if (!callback(context, room_graph, room_app_supervisor))
759    {
760      return false;
761    }
762  }
763
764  return true;
765}
766
767static bool studio_stop_app_supervisor(void * context, ladish_graph_handle graph, ladish_app_supervisor_handle app_supervisor)
768{
769  ladish_app_supervisor_stop(app_supervisor);
770  return true;                  /* iterate all supervisors */
771}
772
773void studio_stop_app_supervisors(void)
774{
775  studio_iterate_virtual_graphs(NULL, studio_stop_app_supervisor);
776}
777
778void emit_studio_renamed()
779{
780  dbus_signal_emit(g_dbus_connection, STUDIO_OBJECT_PATH, IFACE_STUDIO, "StudioRenamed", "s", &g_studio.name);
781}
782
783static void ladish_get_studio_name(struct dbus_method_call * call_ptr)
784{
785  method_return_new_single(call_ptr, DBUS_TYPE_STRING, &g_studio.name);
786}
787
788static void ladish_rename_studio(struct dbus_method_call * call_ptr)
789{
790  const char * new_name;
791  char * new_name_dup;
792 
793  if (!dbus_message_get_args(call_ptr->message, &g_dbus_error, DBUS_TYPE_STRING, &new_name, DBUS_TYPE_INVALID))
794  {
795    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s",  call_ptr->method_name, g_dbus_error.message);
796    dbus_error_free(&g_dbus_error);
797    return;
798  }
799
800  log_info("Rename studio request (%s)", new_name);
801
802  new_name_dup = strdup(new_name);
803  if (new_name_dup == NULL)
804  {
805    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "strdup() failed to allocate new name.");
806    return;
807  }
808
809  free(g_studio.name);
810  g_studio.name = new_name_dup;
811
812  method_return_new_void(call_ptr);
813  emit_studio_renamed();
814}
815
816static bool ladish_save_studio_internal(struct dbus_method_call * call_ptr, const char * new_studio_name)
817{
818  /* FIXME: this is wrong place to do such check because state before
819     command execution needs to be checked and not state before
820     command is submited, but doing it here will show error to
821     user. Once notification mechanism is implemented, the
822     studio_is_started() check in save command run menthod
823     will send a notification and this check must be removed. */
824  if (!studio_is_started())
825  {
826    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Cannot save not-started studio");
827    return false;
828  }
829
830  return ladish_command_save_studio(call_ptr, &g_studio.cmd_queue, new_studio_name);
831}
832
833static void ladish_save_studio(struct dbus_method_call * call_ptr)
834{
835  log_info("Save studio request");
836
837  if (ladish_save_studio_internal(call_ptr, g_studio.name))
838  {
839    method_return_new_void(call_ptr);
840  }
841}
842
843static void ladish_save_as_studio(struct dbus_method_call * call_ptr)
844{
845  const char * new_name;
846
847  log_info("SaveAs studio request");
848
849  if (!dbus_message_get_args(call_ptr->message, &g_dbus_error, DBUS_TYPE_STRING, &new_name, DBUS_TYPE_INVALID))
850  {
851    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s",  call_ptr->method_name, g_dbus_error.message);
852    dbus_error_free(&g_dbus_error);
853    return;
854  }
855
856  if (ladish_save_studio_internal(call_ptr, new_name))
857  {
858    method_return_new_void(call_ptr);
859  }
860}
861
862static void ladish_unload_studio(struct dbus_method_call * call_ptr)
863{
864  log_info("Unload studio request");
865
866  if (ladish_command_unload_studio(call_ptr, &g_studio.cmd_queue))
867  {
868    method_return_new_void(call_ptr);
869  }
870}
871
872static void ladish_stop_studio(struct dbus_method_call * call_ptr)
873{
874  log_info("Stop studio request");
875
876  g_studio.automatic = false;   /* even if it was automatic, it is not anymore because user knows about it */
877
878  if (ladish_command_stop_studio(call_ptr, &g_studio.cmd_queue))
879  {
880    method_return_new_void(call_ptr);
881  }
882}
883
884static void ladish_start_studio(struct dbus_method_call * call_ptr)
885{
886  log_info("Start studio request");
887
888  g_studio.automatic = false;   /* even if it was automatic, it is not anymore because user knows about it */
889
890  if (ladish_command_start_studio(call_ptr, &g_studio.cmd_queue))
891  {
892    method_return_new_void(call_ptr);
893  }
894}
895
896static void ladish_studio_is_started(struct dbus_method_call * call_ptr)
897{
898  dbus_bool_t started;
899
900  started = g_studio.jack_graph_proxy != NULL;
901
902  method_return_new_single(call_ptr, DBUS_TYPE_BOOLEAN, &started);
903}
904
905static
906bool
907add_room_ports(
908  void * context,
909  ladish_port_handle port_handle,
910  const char * port_name,
911  uint32_t port_type,
912  uint32_t port_flags)
913{
914  //log_info("Studio room port \"%s\"", port_name);
915
916  if (JACKDBUS_PORT_IS_INPUT(port_flags))
917  {
918    JACKDBUS_PORT_CLEAR_INPUT(port_flags);
919    JACKDBUS_PORT_SET_OUTPUT(port_flags);
920  }
921  else if (JACKDBUS_PORT_IS_OUTPUT(port_flags))
922  {
923    JACKDBUS_PORT_CLEAR_OUTPUT(port_flags);
924    JACKDBUS_PORT_SET_INPUT(port_flags);
925  }
926  else
927  {
928    log_error("room link port with bad flags %"PRIu32, port_flags);
929    return false;
930  }
931
932  return ladish_graph_add_port(g_studio.studio_graph, context, port_handle, port_name, port_type, port_flags, false);
933}
934
935static void ladish_studio_create_room(struct dbus_method_call * call_ptr)
936{
937  const char * room_name;
938  const char * template_name;
939  ladish_room_handle room;
940  char room_dbus_name[1024];
941  ladish_client_handle room_client;
942  uuid_t room_uuid;
943
944  dbus_error_init(&g_dbus_error);
945
946  if (!dbus_message_get_args(call_ptr->message, &g_dbus_error, DBUS_TYPE_STRING, &room_name, DBUS_TYPE_STRING, &template_name, DBUS_TYPE_INVALID))
947  {
948    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s",  call_ptr->method_name, g_dbus_error.message);
949    dbus_error_free(&g_dbus_error);
950    goto fail;
951  }
952
953  log_info("Request to create new studio room \"%s\" from template \"%s\".", room_name, template_name);
954
955  room = find_room_template_by_name(template_name);
956  if (room == NULL)
957  {
958    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Unknown room template \"%s\"",  template_name);
959    goto fail;
960  }
961
962  g_studio.room_count++;
963
964  sprintf(room_dbus_name, DBUS_BASE_PATH "/Room%u", g_studio.room_count);
965
966  if (!ladish_room_create(NULL, room_name, room, room_dbus_name, &room))
967  {
968    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "ladish_room_create() failed.");
969    goto fail_decrement_room_count;
970  }
971
972  if (g_studio.virtualizer != NULL)
973  {
974    ladish_virtualizer_set_graph_connection_handlers(g_studio.virtualizer, ladish_room_get_graph(room));
975  }
976
977  ladish_room_get_uuid(room, room_uuid);
978
979  if (!ladish_client_create(room_uuid, &room_client))
980  {
981    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "ladish_client_create() failed.");
982    goto fail_destroy_room;
983  }
984
985  if (!ladish_graph_add_client(g_studio.studio_graph, room_client, room_name, false))
986  {
987    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "ladish_graph_add_client() failed to add room client to studio graph.");
988    goto fail_destroy_room_client;
989  }
990
991  if (!ladish_room_iterate_link_ports(room, room_client, add_room_ports))
992  {
993    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Creation of studio room link ports failed.");
994    goto fail_remove_room_client;
995  }
996
997  list_add_tail(ladish_room_get_list_node(room), &g_studio.rooms);
998
999  emit_room_appeared(room);
1000
1001  method_return_new_void(call_ptr);
1002  return;
1003
1004fail_remove_room_client:
1005  ladish_graph_remove_client(g_studio.studio_graph, room_client);
1006fail_destroy_room_client:
1007  ladish_client_destroy(room_client);
1008fail_destroy_room:
1009  ladish_room_destroy(room);
1010fail_decrement_room_count:
1011  g_studio.room_count--;
1012fail:
1013  return;
1014}
1015
1016static void ladish_studio_get_room_list(struct dbus_method_call * call_ptr)
1017{
1018  DBusMessageIter iter, array_iter;
1019  DBusMessageIter struct_iter;
1020  struct list_head * node_ptr;
1021  ladish_room_handle room;
1022
1023  call_ptr->reply = dbus_message_new_method_return(call_ptr->message);
1024  if (call_ptr->reply == NULL)
1025  {
1026    goto fail;
1027  }
1028
1029  dbus_message_iter_init_append(call_ptr->reply, &iter);
1030
1031  if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sa{sv})", &array_iter))
1032  {
1033    goto fail_unref;
1034  }
1035
1036  list_for_each(node_ptr, &g_studio.rooms)
1037  {
1038    room = ladish_room_from_list_node(node_ptr);
1039
1040    if (!dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter))
1041      goto fail_unref;
1042
1043    if (!fill_room_info(&struct_iter, room))
1044      goto fail_unref;
1045
1046    if (!dbus_message_iter_close_container(&array_iter, &struct_iter))
1047      goto fail_unref;
1048  }
1049
1050  if (!dbus_message_iter_close_container(&iter, &array_iter))
1051  {
1052    goto fail_unref;
1053  }
1054
1055  return;
1056
1057fail_unref:
1058  dbus_message_unref(call_ptr->reply);
1059  call_ptr->reply = NULL;
1060
1061fail:
1062  log_error("Ran out of memory trying to construct method return");
1063}
1064
1065static void ladish_studio_delete_room(struct dbus_method_call * call_ptr)
1066{
1067  const char * name;
1068  struct list_head * node_ptr;
1069  ladish_room_handle room;
1070  ladish_client_handle room_client;
1071
1072  dbus_error_init(&g_dbus_error);
1073
1074  if (!dbus_message_get_args(call_ptr->message, &g_dbus_error, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID))
1075  {
1076    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s",  call_ptr->method_name, g_dbus_error.message);
1077    dbus_error_free(&g_dbus_error);
1078    return;
1079  }
1080
1081  log_info("Delete studio room request (%s)", name);
1082
1083  list_for_each(node_ptr, &g_studio.rooms)
1084  {
1085    room = ladish_room_from_list_node(node_ptr);
1086    if (strcmp(ladish_room_get_name(room), name) == 0)
1087    {
1088      list_del(node_ptr);
1089      g_studio.room_count--;
1090      emit_room_disappeared(room);
1091
1092      room_client = ladish_graph_find_client_by_name(g_studio.studio_graph, ladish_room_get_name(room));
1093      ASSERT(room_client != NULL);
1094      ladish_graph_remove_client(g_studio.studio_graph, room_client);
1095      ladish_client_destroy(room_client);
1096
1097      ladish_room_destroy(room);
1098      method_return_new_void(call_ptr);
1099      return;
1100    }
1101  }
1102
1103  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);
1104  return;
1105}
1106
1107void studio_remove_all_rooms(void)
1108{
1109  struct list_head * node_ptr;
1110  ladish_room_handle room;
1111
1112  while (!list_empty(&g_studio.rooms))
1113  {
1114    node_ptr = g_studio.rooms.next;
1115    list_del(node_ptr);
1116    room = ladish_room_from_list_node(node_ptr);
1117    ASSERT(g_studio.room_count > 0);
1118    g_studio.room_count--;
1119    emit_room_disappeared(room);
1120    ladish_room_destroy(room);
1121  }
1122
1123  ASSERT(g_studio.room_count == 0);
1124}
1125
1126METHOD_ARGS_BEGIN(GetName, "Get studio name")
1127  METHOD_ARG_DESCRIBE_OUT("studio_name", "s", "Name of studio")
1128METHOD_ARGS_END
1129
1130METHOD_ARGS_BEGIN(Rename, "Rename studio")
1131  METHOD_ARG_DESCRIBE_IN("studio_name", "s", "New name")
1132METHOD_ARGS_END
1133
1134METHOD_ARGS_BEGIN(Save, "Save studio")
1135METHOD_ARGS_END
1136
1137METHOD_ARGS_BEGIN(SaveAs, "SaveAs studio")
1138  METHOD_ARG_DESCRIBE_IN("studio_name", "s", "New name")
1139METHOD_ARGS_END
1140
1141METHOD_ARGS_BEGIN(Unload, "Unload studio")
1142METHOD_ARGS_END
1143
1144METHOD_ARGS_BEGIN(Start, "Start studio")
1145METHOD_ARGS_END
1146
1147METHOD_ARGS_BEGIN(Stop, "Stop studio")
1148METHOD_ARGS_END
1149
1150METHOD_ARGS_BEGIN(IsStarted, "Check whether studio is started")
1151  METHOD_ARG_DESCRIBE_OUT("started", "b", "Whether studio is started")
1152METHOD_ARGS_END
1153
1154METHOD_ARGS_BEGIN(CreateRoom, "Create new studio room")
1155  METHOD_ARG_DESCRIBE_IN("room_name", "s", "Studio room name")
1156  METHOD_ARG_DESCRIBE_IN("room_template_name", "s", "Room template name")
1157METHOD_ARGS_END
1158
1159METHOD_ARGS_BEGIN(GetRoomList, "Get list of rooms in this studio")
1160  METHOD_ARG_DESCRIBE_OUT("room_list", "a(sa{sv})", "List of studio rooms: opaths and properties")
1161METHOD_ARGS_END
1162
1163METHOD_ARGS_BEGIN(DeleteRoom, "Delete studio room")
1164  METHOD_ARG_DESCRIBE_IN("room_name", "s", "Name of studio room to delete")
1165METHOD_ARGS_END
1166
1167METHODS_BEGIN
1168  METHOD_DESCRIBE(GetName, ladish_get_studio_name)
1169  METHOD_DESCRIBE(Rename, ladish_rename_studio)
1170  METHOD_DESCRIBE(Save, ladish_save_studio)
1171  METHOD_DESCRIBE(SaveAs, ladish_save_as_studio)
1172  METHOD_DESCRIBE(Unload, ladish_unload_studio)
1173  METHOD_DESCRIBE(Start, ladish_start_studio)
1174  METHOD_DESCRIBE(Stop, ladish_stop_studio)
1175  METHOD_DESCRIBE(IsStarted, ladish_studio_is_started)
1176  METHOD_DESCRIBE(CreateRoom, ladish_studio_create_room)
1177  METHOD_DESCRIBE(GetRoomList, ladish_studio_get_room_list)
1178  METHOD_DESCRIBE(DeleteRoom, ladish_studio_delete_room)
1179METHODS_END
1180
1181SIGNAL_ARGS_BEGIN(StudioRenamed, "Studio name changed")
1182  SIGNAL_ARG_DESCRIBE("studio_name", "s", "New studio name")
1183SIGNAL_ARGS_END
1184
1185SIGNAL_ARGS_BEGIN(StudioStarted, "Studio started")
1186SIGNAL_ARGS_END
1187
1188SIGNAL_ARGS_BEGIN(StudioCrashed, "Studio crashed")
1189SIGNAL_ARGS_END
1190
1191SIGNAL_ARGS_BEGIN(StudioStopped, "Studio stopped")
1192SIGNAL_ARGS_END
1193
1194SIGNAL_ARGS_BEGIN(RoomAppeared, "Room D-Bus object appeared")
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(RoomChanged, "Room D-Bus object changed")
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
1204SIGNAL_ARGS_BEGIN(RoomDisappeared, "Room D-Bus object disappeared")
1205  SIGNAL_ARG_DESCRIBE("opath", "s", "room object path")
1206  SIGNAL_ARG_DESCRIBE("properties", "a{sv}", "room object path and props")
1207SIGNAL_ARGS_END
1208
1209SIGNALS_BEGIN
1210  SIGNAL_DESCRIBE(StudioRenamed)
1211  SIGNAL_DESCRIBE(StudioStarted)
1212  SIGNAL_DESCRIBE(StudioCrashed)
1213  SIGNAL_DESCRIBE(StudioStopped)
1214  SIGNAL_DESCRIBE(RoomAppeared)
1215  SIGNAL_DESCRIBE(RoomDisappeared)
1216  SIGNAL_DESCRIBE(RoomChanged)
1217SIGNALS_END
1218
1219INTERFACE_BEGIN(g_interface_studio, IFACE_STUDIO)
1220  INTERFACE_DEFAULT_HANDLER
1221  INTERFACE_EXPOSE_METHODS
1222  INTERFACE_EXPOSE_SIGNALS
1223INTERFACE_END
Note: See TracBrowser for help on using the browser.