root/daemon/studio.c @ 5d0c8c714ba4f21d9cf3b4418f67a7b0f556b8d2

Revision 5d0c8c714ba4f21d9cf3b4418f67a7b0f556b8d2, 15.0 KB (checked in by Nedko Arnaudov <nedko@…>, 4 years ago)

ladishd: fix studio save

  • Property mode set to 100644
Line 
1/* -*- Mode: C ; c-basic-offset: 2 -*- */
2/*
3 * LADI Session Handler (ladish)
4 *
5 * Copyright (C) 2009 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
43#define STUDIOS_DIR "/studios/"
44char * g_studios_dir;
45
46struct studio g_studio;
47
48bool studio_name_generate(char ** name_ptr)
49{
50  time_t now;
51  char timestamp_str[26];
52  char * name;
53
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);
58
59  name = catdup("Studio ", timestamp_str);
60  if (name == NULL)
61  {
62    log_error("catdup failed to create studio name");
63    return false;
64  }
65
66  *name_ptr = name;
67  return true;
68}
69
70bool
71studio_publish(void)
72{
73  dbus_object_path object;
74
75  ASSERT(g_studio.name != NULL);
76
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    NULL);
83  if (object == NULL)
84  {
85    log_error("dbus_object_path_new() failed");
86    return false;
87  }
88
89  if (!dbus_object_path_register(g_dbus_connection, object))
90  {
91    log_error("object_path_register() failed");
92    dbus_object_path_destroy(g_dbus_connection, object);
93    return false;
94  }
95
96  log_info("Studio D-Bus object created. \"%s\"", g_studio.name);
97
98  g_studio.dbus_object = object;
99
100  emit_studio_appeared();
101
102  return true;
103}
104
105void emit_studio_started()
106{
107  dbus_signal_emit(g_dbus_connection, STUDIO_OBJECT_PATH, IFACE_STUDIO, "StudioStarted", "");
108}
109
110void emit_studio_stopped()
111{
112  dbus_signal_emit(g_dbus_connection, STUDIO_OBJECT_PATH, IFACE_STUDIO, "StudioStopped", "");
113}
114
115void on_event_jack_started(void)
116{
117  if (g_studio.dbus_object == NULL)
118  {
119    ASSERT(g_studio.name == NULL);
120    if (!studio_name_generate(&g_studio.name))
121    {
122      log_error("studio_name_generate() failed.");
123      return;
124    }
125
126    g_studio.automatic = true;
127
128    studio_publish();
129  }
130
131  if (!studio_fetch_jack_settings())
132  {
133    log_error("studio_fetch_jack_settings() failed.");
134
135    return;
136  }
137
138  log_info("jack conf successfully retrieved");
139  g_studio.jack_conf_valid = true;
140
141  if (!graph_proxy_create(JACKDBUS_SERVICE_NAME, JACKDBUS_OBJECT_PATH, false, &g_studio.jack_graph_proxy))
142  {
143    log_error("graph_proxy_create() failed for jackdbus");
144  }
145  else
146  {
147    if (!ladish_jack_dispatcher_create(g_studio.jack_graph_proxy, g_studio.jack_graph, g_studio.studio_graph, &g_studio.jack_dispatcher))
148    {
149      log_error("ladish_jack_dispatcher_create() failed.");
150    }
151
152    if (!graph_proxy_activate(g_studio.jack_graph_proxy))
153    {
154      log_error("graph_proxy_activate() failed.");
155    }
156  }
157
158  emit_studio_started();
159}
160
161void on_event_jack_stopped(void)
162{
163  emit_studio_stopped();
164
165  if (g_studio.automatic)
166  {
167    log_info("Unloading automatic studio.");
168    ladish_command_unload_studio(NULL, &g_studio.cmd_queue);
169    return;
170  }
171
172  if (g_studio.jack_dispatcher)
173  {
174    ladish_jack_dispatcher_destroy(g_studio.jack_dispatcher);
175    g_studio.jack_dispatcher = NULL;
176  }
177
178  if (g_studio.jack_graph_proxy)
179  {
180    graph_proxy_destroy(g_studio.jack_graph_proxy);
181    g_studio.jack_graph_proxy = NULL;
182  }
183
184  /* TODO: if user wants, restart jack server and reconnect all jack apps to it */
185}
186
187void studio_run(void)
188{
189  bool state;
190
191  ladish_cqueue_run(&g_studio.cmd_queue);
192
193  if (ladish_environment_consume_change(&g_studio.env_store, ladish_environment_jack_server_started, &state))
194  {
195    ladish_cqueue_clear(&g_studio.cmd_queue);
196
197    if (state)
198    {
199      on_event_jack_started();
200    }
201    else
202    {
203      on_event_jack_stopped();
204    }
205  }
206
207  ladish_environment_ignore(&g_studio.env_store, ladish_environment_jack_server_present);
208  ladish_environment_assert_consumed(&g_studio.env_store);
209}
210
211static void on_jack_server_started(void)
212{
213  log_info("JACK server start detected.");
214  ladish_environment_set(&g_studio.env_store, ladish_environment_jack_server_started);
215}
216
217static void on_jack_server_stopped(void)
218{
219  ladish_environment_reset(&g_studio.env_store, ladish_environment_jack_server_started);
220}
221
222static void on_jack_server_appeared(void)
223{
224  log_info("JACK controller appeared.");
225  ladish_environment_set(&g_studio.env_store, ladish_environment_jack_server_present);
226}
227
228static void on_jack_server_disappeared(void)
229{
230  log_info("JACK controller disappeared.");
231  ladish_environment_reset(&g_studio.env_store, ladish_environment_jack_server_present);
232}
233
234bool studio_init(void)
235{
236  log_info("studio object construct");
237
238  g_studios_dir = catdup(g_base_dir, STUDIOS_DIR);
239  if (g_studios_dir == NULL)
240  {
241    log_error("catdup failed for '%s' and '%s'", g_base_dir, STUDIOS_DIR);
242    goto fail;
243  }
244
245  if (!ensure_dir_exist(g_studios_dir, 0700))
246  {
247    goto free_studios_dir;
248  }
249
250  INIT_LIST_HEAD(&g_studio.all_connections);
251  INIT_LIST_HEAD(&g_studio.all_ports);
252  INIT_LIST_HEAD(&g_studio.all_clients);
253  INIT_LIST_HEAD(&g_studio.jack_connections);
254  INIT_LIST_HEAD(&g_studio.jack_ports);
255  INIT_LIST_HEAD(&g_studio.jack_clients);
256  INIT_LIST_HEAD(&g_studio.rooms);
257  INIT_LIST_HEAD(&g_studio.clients);
258  INIT_LIST_HEAD(&g_studio.ports);
259
260  INIT_LIST_HEAD(&g_studio.jack_conf);
261  INIT_LIST_HEAD(&g_studio.jack_params);
262
263  g_studio.dbus_object = NULL;
264  g_studio.name = NULL;
265  g_studio.filename = NULL;
266
267  if (!ladish_graph_create(&g_studio.jack_graph, NULL))
268  {
269    log_error("ladish_graph_create() failed to create jack graph object.");
270    goto free_studios_dir;
271  }
272
273  if (!ladish_graph_create(&g_studio.studio_graph, STUDIO_OBJECT_PATH))
274  {
275    log_error("ladish_graph_create() failed to create studio graph object.");
276    goto jack_graph_destroy;
277  }
278
279  ladish_cqueue_init(&g_studio.cmd_queue);
280  ladish_environment_init(&g_studio.env_store);
281
282  if (!jack_proxy_init(
283        on_jack_server_started,
284        on_jack_server_stopped,
285        on_jack_server_appeared,
286        on_jack_server_disappeared))
287  {
288    log_error("jack_proxy_init() failed.");
289    goto studio_graph_destroy;
290  }
291
292  return true;
293
294studio_graph_destroy:
295  ladish_graph_destroy(g_studio.studio_graph, false);
296jack_graph_destroy:
297  ladish_graph_destroy(g_studio.jack_graph, false);
298free_studios_dir:
299  free(g_studios_dir);
300fail:
301  return false;
302}
303
304void studio_uninit(void)
305{
306  log_info("studio_uninit()");
307
308  jack_proxy_uninit();
309
310  ladish_cqueue_clear(&g_studio.cmd_queue);
311
312  free(g_studios_dir);
313
314  log_info("studio object destroy");
315}
316
317bool studio_is_loaded(void)
318{
319  return g_studio.dbus_object != NULL;
320}
321
322bool studio_is_started(void)
323{
324  return ladish_environment_get(&g_studio.env_store, ladish_environment_jack_server_started);
325}
326
327bool studio_compose_filename(const char * name, char ** filename_ptr_ptr, char ** backup_filename_ptr_ptr)
328{
329  size_t len_dir;
330  char * p;
331  const char * src;
332  char * filename_ptr;
333  char * backup_filename_ptr = NULL;
334
335  len_dir = strlen(g_studios_dir);
336
337  filename_ptr = malloc(len_dir + 1 + strlen(name) * 3 + 4 + 1);
338  if (filename_ptr == NULL)
339  {
340    log_error("malloc failed to allocate memory for studio file path");
341    return false;
342  }
343
344  if (backup_filename_ptr_ptr != NULL)
345  {
346    backup_filename_ptr = malloc(len_dir + 1 + strlen(name) * 3 + 4 + 4 + 1);
347    if (backup_filename_ptr == NULL)
348    {
349      log_error("malloc failed to allocate memory for studio backup file path");
350      free(filename_ptr);
351      return false;
352    }
353  }
354
355  p = filename_ptr;
356  memcpy(p, g_studios_dir, len_dir);
357  p += len_dir;
358
359  *p++ = '/';
360
361  src = name;
362  escape(&src, &p);
363  strcpy(p, ".xml");
364
365  *filename_ptr_ptr = filename_ptr;
366
367  if (backup_filename_ptr_ptr != NULL)
368  {
369    strcpy(backup_filename_ptr, filename_ptr);
370    strcat(backup_filename_ptr, ".bak");
371    *backup_filename_ptr_ptr = backup_filename_ptr;
372  }
373
374  return true;
375}
376
377bool studios_iterate(void * call_ptr, void * context, bool (* callback)(void * call_ptr, void * context, const char * studio, uint32_t modtime))
378{
379  DIR * dir;
380  struct dirent * dentry;
381  size_t len;
382  struct stat st;
383  char * path;
384  char * name;
385
386  dir = opendir(g_studios_dir);
387  if (dir == NULL)
388  {
389    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Cannot open directory '%s': %d (%s)", g_studios_dir, errno, strerror(errno));
390    return false;
391  }
392
393  while ((dentry = readdir(dir)) != NULL)
394  {
395    if (dentry->d_type != DT_REG)
396      continue;
397
398    len = strlen(dentry->d_name);
399    if (len <= 4 || strcmp(dentry->d_name + (len - 4), ".xml") != 0)
400      continue;
401
402    path = catdup(g_studios_dir, dentry->d_name);
403    if (path == NULL)
404    {
405      lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "catdup() failed");
406      return false;
407    }
408
409    if (stat(path, &st) != 0)
410    {
411      lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "failed to stat '%s': %d (%s)", path, errno, strerror(errno));
412      free(path);
413      return false;
414    }
415
416    free(path);
417
418    name = malloc(len - 4 + 1);
419    name[unescape(dentry->d_name, len - 4, name)] = 0;
420    //log_info("name = '%s'", name);
421
422    if (!callback(call_ptr, context, name, st.st_mtime))
423    {
424      free(name);
425      closedir(dir);
426      return false;
427    }
428
429    free(name);
430  }
431
432  closedir(dir);
433  return true;
434}
435
436bool studio_delete(void * call_ptr, const char * studio_name)
437{
438  char * filename;
439  char * bak_filename;
440  struct stat st;
441  bool ret;
442
443  ret = false;
444
445  if (!studio_compose_filename(studio_name, &filename, &bak_filename))
446  {
447    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "failed to compose studio filename");
448    goto exit;
449  }
450
451  log_info("Deleting studio ('%s')", filename);
452
453  if (unlink(filename) != 0)
454  {
455    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "unlink(%s) failed: %d (%s)", filename, errno, strerror(errno));
456    goto free;
457  }
458
459  /* try to delete the backup file */
460  if (stat(bak_filename, &st) == 0)
461  {
462    if (unlink(bak_filename) != 0)
463    {
464      /* failing to delete backup file will not case delete command failure */
465      log_error("unlink(%s) failed: %d (%s)", bak_filename, errno, strerror(errno));
466    }
467  }
468
469  ret = true;
470
471free:
472  free(filename);
473  free(bak_filename);
474exit:
475  return ret;
476}
477
478void emit_studio_renamed()
479{
480  dbus_signal_emit(g_dbus_connection, STUDIO_OBJECT_PATH, IFACE_STUDIO, "StudioRenamed", "s", &g_studio.name);
481}
482
483static void ladish_get_studio_name(struct dbus_method_call * call_ptr)
484{
485  method_return_new_single(call_ptr, DBUS_TYPE_STRING, &g_studio.name);
486}
487
488static void ladish_rename_studio(struct dbus_method_call * call_ptr)
489{
490  const char * new_name;
491  char * new_name_dup;
492 
493  if (!dbus_message_get_args(call_ptr->message, &g_dbus_error, DBUS_TYPE_STRING, &new_name, DBUS_TYPE_INVALID))
494  {
495    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s",  call_ptr->method_name, g_dbus_error.message);
496    dbus_error_free(&g_dbus_error);
497    return;
498  }
499
500  log_info("Rename studio request (%s)", new_name);
501
502  new_name_dup = strdup(new_name);
503  if (new_name_dup == NULL)
504  {
505    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "strdup() failed to allocate new name.");
506    return;
507  }
508
509  free(g_studio.name);
510  g_studio.name = new_name_dup;
511
512  method_return_new_void(call_ptr);
513  emit_studio_renamed();
514}
515
516static void ladish_save_studio(struct dbus_method_call * call_ptr)
517{
518  log_info("Save studio request");
519
520  if (ladish_command_save_studio(call_ptr, &g_studio.cmd_queue))
521  {
522    method_return_new_void(call_ptr);
523  }
524}
525
526static void ladish_unload_studio(struct dbus_method_call * call_ptr)
527{
528  log_info("Unload studio request");
529
530  if (ladish_command_unload_studio(call_ptr, &g_studio.cmd_queue))
531  {
532    method_return_new_void(call_ptr);
533  }
534}
535
536static void ladish_stop_studio(struct dbus_method_call * call_ptr)
537{
538  log_info("Stop studio request");
539
540  g_studio.automatic = false;   /* even if it was automatic, it is not anymore because user knows about it */
541
542  if (ladish_command_stop_studio(call_ptr, &g_studio.cmd_queue))
543  {
544    method_return_new_void(call_ptr);
545  }
546}
547
548static void ladish_start_studio(struct dbus_method_call * call_ptr)
549{
550  log_info("Start studio request");
551
552  g_studio.automatic = false;   /* even if it was automatic, it is not anymore because user knows about it */
553
554  if (ladish_command_start_studio(call_ptr, &g_studio.cmd_queue))
555  {
556    method_return_new_void(call_ptr);
557  }
558}
559
560METHOD_ARGS_BEGIN(GetName, "Get studio name")
561  METHOD_ARG_DESCRIBE_OUT("studio_name", "s", "Name of studio")
562METHOD_ARGS_END
563
564METHOD_ARGS_BEGIN(Rename, "Rename studio")
565  METHOD_ARG_DESCRIBE_IN("studio_name", "s", "New name")
566METHOD_ARGS_END
567
568METHOD_ARGS_BEGIN(Save, "Save studio")
569METHOD_ARGS_END
570
571METHOD_ARGS_BEGIN(Unload, "Unload studio")
572METHOD_ARGS_END
573
574METHOD_ARGS_BEGIN(Start, "Start studio")
575METHOD_ARGS_END
576
577METHOD_ARGS_BEGIN(Stop, "Stop studio")
578METHOD_ARGS_END
579
580METHODS_BEGIN
581  METHOD_DESCRIBE(GetName, ladish_get_studio_name)
582  METHOD_DESCRIBE(Rename, ladish_rename_studio)
583  METHOD_DESCRIBE(Save, ladish_save_studio)
584  METHOD_DESCRIBE(Unload, ladish_unload_studio)
585  METHOD_DESCRIBE(Start, ladish_start_studio)
586  METHOD_DESCRIBE(Stop, ladish_stop_studio)
587METHODS_END
588
589SIGNAL_ARGS_BEGIN(StudioRenamed, "Studio name changed")
590  SIGNAL_ARG_DESCRIBE("studio_name", "s", "New studio name")
591SIGNAL_ARGS_END
592
593SIGNAL_ARGS_BEGIN(StudioStarted, "Studio started")
594SIGNAL_ARGS_END
595
596SIGNAL_ARGS_BEGIN(StudioStopped, "Studio stopped")
597SIGNAL_ARGS_END
598
599SIGNAL_ARGS_BEGIN(RoomAppeared, "Room D-Bus object appeared")
600  SIGNAL_ARG_DESCRIBE("room_path", "s", "room object path")
601SIGNAL_ARGS_END
602
603SIGNAL_ARGS_BEGIN(RoomDisappeared, "Room D-Bus object disappeared")
604  SIGNAL_ARG_DESCRIBE("room_path", "s", "room object path")
605SIGNAL_ARGS_END
606
607SIGNALS_BEGIN
608  SIGNAL_DESCRIBE(StudioRenamed)
609  SIGNAL_DESCRIBE(StudioStarted)
610  SIGNAL_DESCRIBE(StudioStopped)
611  SIGNAL_DESCRIBE(RoomAppeared)
612  SIGNAL_DESCRIBE(RoomDisappeared)
613SIGNALS_END
614
615INTERFACE_BEGIN(g_interface_studio, IFACE_STUDIO)
616  INTERFACE_DEFAULT_HANDLER
617  INTERFACE_EXPOSE_METHODS
618  INTERFACE_EXPOSE_SIGNALS
619INTERFACE_END
Note: See TracBrowser for help on using the browser.