root/daemon/cmd_save_studio.c @ 5483a52daf9dca6aa6cbfafc7d6d050456d49dd3

Revision 5483a52daf9dca6aa6cbfafc7d6d050456d49dd3, 12.5 KB (checked in by Nedko Arnaudov <nedko@…>, 20 months ago)

ladishd: async app supervisor save. async studio and project 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,2010,2011 Nedko Arnaudov <nedko@arnaudov.name>
6 *
7 **************************************************************************
8 * This file contains implementation of the "save studio" command
9 **************************************************************************
10 *
11 * LADI Session Handler is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * LADI Session Handler is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with LADI Session Handler. If not, see <http://www.gnu.org/licenses/>
23 * or write to the Free Software Foundation, Inc.,
24 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
25 */
26
27#include <sys/types.h>
28#include <signal.h>
29
30#include "common.h"
31
32#include <sys/types.h>
33#include <sys/stat.h>
34#include <fcntl.h>
35#include <unistd.h>
36
37#include "escape.h"
38#include "studio_internal.h"
39#include "cmd.h"
40#include "../proxies/notify_proxy.h"
41#include "save.h"
42
43#define STUDIO_HEADER_TEXT BASE_NAME " Studio configuration.\n"
44
45bool
46write_jack_parameter(
47  int fd,
48  int indent,
49  struct jack_conf_parameter * parameter_ptr)
50{
51  const char * src;
52  char * dst;
53  char path[max_escaped_length(JACK_CONF_MAX_ADDRESS_SIZE)];
54  const char * content;
55  char valbuf[100];
56
57  /* compose the parameter path, percent-encode "bad" chars */
58  src = parameter_ptr->address;
59  dst = path;
60  do
61  {
62    *dst++ = '/';
63    escape(&src, &dst, LADISH_ESCAPE_FLAG_ALL);
64    src++;
65  }
66  while (*src != 0);
67  *dst = 0;
68
69  if (!ladish_write_indented_string(fd, indent, "<parameter path=\""))
70  {
71    return false;
72  }
73
74  if (!ladish_write_string(fd, path))
75  {
76    return false;
77  }
78
79  if (!ladish_write_string(fd, "\">"))
80  {
81    return false;
82  }
83
84  switch (parameter_ptr->parameter.type)
85  {
86  case jack_boolean:
87    content = parameter_ptr->parameter.value.boolean ? "true" : "false";
88    log_debug("%s value is %s (boolean)", path, content);
89    break;
90  case jack_string:
91    content = parameter_ptr->parameter.value.string;
92    log_debug("%s value is %s (string)", path, content);
93    break;
94  case jack_byte:
95    valbuf[0] = (char)parameter_ptr->parameter.value.byte;
96    valbuf[1] = 0;
97    content = valbuf;
98    log_debug("%s value is %u/%c (byte/char)", path, parameter_ptr->parameter.value.byte, (char)parameter_ptr->parameter.value.byte);
99    break;
100  case jack_uint32:
101    snprintf(valbuf, sizeof(valbuf), "%" PRIu32, parameter_ptr->parameter.value.uint32);
102    content = valbuf;
103    log_debug("%s value is %s (uint32)", path, content);
104    break;
105  case jack_int32:
106    snprintf(valbuf, sizeof(valbuf), "%" PRIi32, parameter_ptr->parameter.value.int32);
107    content = valbuf;
108    log_debug("%s value is %s (int32)", path, content);
109    break;
110  default:
111    log_error("unknown jack parameter_ptr->parameter type %d (%s)", (int)parameter_ptr->parameter.type, path);
112    return false;
113  }
114
115  if (!ladish_write_string(fd, content))
116  {
117    return false;
118  }
119
120  if (!ladish_write_string(fd, "</parameter>\n"))
121  {
122    return false;
123  }
124
125  return true;
126}
127
128#define fd (((struct ladish_write_context *)context)->fd)
129#define indent (((struct ladish_write_context *)context)->indent)
130
131static bool save_studio_room(void * context, ladish_room_handle room)
132{
133  uuid_t uuid;
134  char str[37];
135
136  log_info("saving room '%s'", ladish_room_get_name(room));
137
138  if (!ladish_write_indented_string(fd, indent, "<room name=\""))
139  {
140    return false;
141  }
142
143  if (!ladish_write_string(fd, ladish_room_get_name(room)))
144  {
145    return false;
146  }
147
148  if (!ladish_write_string(fd, "\" uuid=\""))
149  {
150    return false;
151  }
152
153  ladish_room_get_uuid(room, uuid);
154  uuid_unparse(uuid, str);
155
156  if (!ladish_write_string(fd, str))
157  {
158    return false;
159  }
160
161  if (!ladish_write_string(fd, "\">\n"))
162  {
163    return false;
164  }
165
166  if (!ladish_write_room_link_ports(fd, indent + 1, room))
167  {
168    log_error("ladish_write_room_link_ports() failed");
169    return false;
170  }
171
172  if (!ladish_write_indented_string(fd, indent, " </room>\n"))
173  {
174    return false;
175  }
176
177  return true;
178}
179
180#undef indent
181#undef fd
182
183struct ladish_command_save_studio
184{
185  struct ladish_command command;
186  char * studio_name;
187  bool done;
188  bool success;
189};
190
191static bool ladish_save_studio_xml(struct ladish_command_save_studio * cmd_ptr)
192{
193  struct list_head * node_ptr;
194  struct jack_conf_parameter * parameter_ptr;
195  int fd;
196  time_t timestamp;
197  char timestamp_str[26];
198  bool ret;
199  char * filename;              /* filename */
200  char * bak_filename;          /* filename of the backup file */
201  char * old_filename;          /* filename where studio was persisted before save */
202  struct stat st;
203  struct ladish_write_context save_context;
204  bool renaming;
205
206  time(&timestamp);
207  ctime_r(&timestamp, timestamp_str);
208  timestamp_str[24] = 0;
209
210  if (!ladish_studio_compose_filename(cmd_ptr->studio_name, &filename, &bak_filename))
211  {
212    log_error("failed to compose studio filename");
213    goto exit;
214  }
215
216  /* whether save will initiate a rename */
217  renaming = strcmp(cmd_ptr->studio_name, g_studio.name) != 0;
218
219  if (g_studio.filename == NULL)
220  {
221    /* saving studio for first time */
222    g_studio.filename = filename;
223    free(bak_filename);
224    bak_filename = NULL;
225    old_filename = NULL;
226  }
227  else if (strcmp(g_studio.filename, filename) == 0)
228  {
229    /* saving already persisted studio that was not renamed */
230    old_filename = filename;
231  }
232  else if (!renaming)
233  {
234    /* saving already renamed studio */
235    old_filename = g_studio.filename;
236    g_studio.filename = filename;
237  }
238  else
239  {
240    /* saving studio copy (save as) */
241    old_filename = filename;
242    g_studio.filename = filename;
243  }
244
245  filename = NULL;
246  ASSERT(g_studio.filename != NULL);
247  ASSERT(g_studio.filename != bak_filename);
248
249  if (bak_filename != NULL)
250  {
251    ASSERT(old_filename != NULL);
252
253    if (stat(old_filename, &st) == 0) /* if old filename does not exist, rename with fail */
254    {
255      if (rename(old_filename, bak_filename) != 0)
256      {
257        log_error("rename(%s, %s) failed: %d (%s)", old_filename, bak_filename, errno, strerror(errno));
258        goto free_filenames;
259      }
260    }
261    else
262    {
263      /* mark that there is no backup file */
264      free(bak_filename);
265      bak_filename = NULL;
266    }
267  }
268
269  log_info("saving studio... (%s)", g_studio.filename);
270
271  fd = open(g_studio.filename, O_WRONLY | O_TRUNC | O_CREAT, 0666);
272  if (fd == -1)
273  {
274    log_error("open(%s) failed: %d (%s)", g_studio.filename, errno, strerror(errno));
275    goto rename_back;
276  }
277
278  if (!ladish_write_string(fd, "<?xml version=\"1.0\"?>\n"))
279  {
280    goto close;
281  }
282
283  if (!ladish_write_string(fd, "<!--\n"))
284  {
285    goto close;
286  }
287
288  if (!ladish_write_string(fd, STUDIO_HEADER_TEXT))
289  {
290    goto close;
291  }
292
293  if (!ladish_write_string(fd, "-->\n"))
294  {
295    goto close;
296  }
297
298  if (!ladish_write_string(fd, "<!-- "))
299  {
300    goto close;
301  }
302
303  if (!ladish_write_string(fd, timestamp_str))
304  {
305    goto close;
306  }
307
308  if (!ladish_write_string(fd, " -->\n"))
309  {
310    goto close;
311  }
312
313  if (!ladish_write_string(fd, "<studio>\n"))
314  {
315    goto close;
316  }
317
318  if (!ladish_write_indented_string(fd, 1, "<jack>\n"))
319  {
320    goto close;
321  }
322
323  if (!ladish_write_indented_string(fd, 2, "<conf>\n"))
324  {
325    goto close;
326  }
327
328  list_for_each(node_ptr, &g_studio.jack_params)
329  {
330    parameter_ptr = list_entry(node_ptr, struct jack_conf_parameter, leaves);
331
332    if (!write_jack_parameter(fd, 3, parameter_ptr))
333    {
334      goto close;
335    }
336  }
337
338  if (!ladish_write_indented_string(fd, 2, "</conf>\n"))
339  {
340    goto close;
341  }
342
343  if (!ladish_write_jgraph(fd, 2, ladish_studio_get_studio_graph(), ladish_studio_get_studio_app_supervisor()))
344  {
345    log_error("ladish_write_jgraph() failed for studio graph");
346    goto close;
347  }
348
349  if (!ladish_write_indented_string(fd, 1, "</jack>\n"))
350  {
351    goto close;
352  }
353
354  if (ladish_studio_has_rooms())
355  {
356    if (!ladish_write_indented_string(fd, 1, "<rooms>\n"))
357    {
358      goto close;
359    }
360
361    save_context.indent = 2;
362    save_context.fd = fd;
363
364    if (!ladish_studio_iterate_rooms(&save_context, save_studio_room))
365    {
366      log_error("ladish_studio_iterate_rooms() failed");
367      goto close;
368    }
369
370    if (!ladish_write_indented_string(fd, 1, "</rooms>\n"))
371    {
372      goto close;
373    }
374  }
375
376  if (!ladish_write_vgraph(fd, 1, g_studio.studio_graph, g_studio.app_supervisor))
377  {
378    log_error("ladish_write_vgraph() failed for studio");
379    goto close;
380  }
381
382  if (!ladish_write_dict(fd, 1, ladish_graph_get_dict(g_studio.studio_graph)))
383  {
384    goto close;
385  }
386
387  if (!ladish_write_string(fd, "</studio>\n"))
388  {
389    goto close;
390  }
391
392  log_info("studio saved. (%s)", g_studio.filename);
393  g_studio.persisted = true;
394  g_studio.automatic = false;   /* even if it was automatic, it is not anymore because it is saved */
395
396  ret = true;
397
398  if (renaming)
399  {
400    free(g_studio.name);
401    g_studio.name = cmd_ptr->studio_name;
402    cmd_ptr->studio_name = NULL; /* mark that descructor does not need to free the new name buffer */
403    ladish_studio_emit_renamed(); /* uses g_studio.name */
404  }
405
406close:
407  close(fd);
408
409rename_back:
410  if (!ret && bak_filename != NULL)
411  {
412    /* save failed - try to rename the backup file back */
413    ASSERT(old_filename != NULL);
414    if (rename(bak_filename, old_filename) != 0)
415    {
416      log_error("rename(%s, %s) failed: %d (%s)", bak_filename, g_studio.filename, errno, strerror(errno));
417    }
418  }
419
420free_filenames:
421  if (bak_filename != NULL)
422  {
423    free(bak_filename);
424  }
425
426  if (old_filename != NULL && old_filename != g_studio.filename)
427  {
428    free(old_filename);
429  }
430
431  ASSERT(filename == NULL);
432  ASSERT(g_studio.filename != NULL);
433
434exit:
435  return ret;
436}
437
438#define cmd_ptr ((struct ladish_command_save_studio *)context)
439
440static void ladish_studio_apps_save_complete(void * context, bool success)
441{
442  ASSERT(cmd_ptr->command.state == LADISH_COMMAND_STATE_WAITING);
443
444  if (!success)
445  {
446    log_info("Studio apps save failed");
447    goto fail;
448  }
449
450  log_info("Studio apps saved successfully");
451  if (ladish_save_studio_xml(cmd_ptr))
452  {
453    goto done;
454  }
455
456fail:
457  ladish_notify_simple(LADISH_NOTIFY_URGENCY_HIGH, "Studio save failed", LADISH_CHECK_LOG_TEXT);
458  cmd_ptr->success = false;
459
460done:
461  cmd_ptr->done = true;
462  return;
463}
464
465#undef cmd_ptr
466
467#define cmd_ptr ((struct ladish_command_save_studio *)command_context)
468
469static bool run(void * command_context)
470{
471  if (cmd_ptr->command.state == LADISH_COMMAND_STATE_WAITING)
472  {
473    if (!cmd_ptr->done)
474    {
475      return true;
476    }
477
478    cmd_ptr->command.state = LADISH_COMMAND_STATE_DONE;
479    return cmd_ptr->success;
480  }
481
482  if (cmd_ptr->command.state != LADISH_COMMAND_STATE_PENDING)
483  {
484    ASSERT_NO_PASS;
485    return false;
486  }
487
488  if (!ladish_studio_is_started())
489  {
490    log_error("Cannot save not-started studio");
491    ladish_notify_simple(LADISH_NOTIFY_URGENCY_HIGH, "Cannot save not-started studio", NULL);
492    return false;
493  }
494
495  ladish_check_integrity();
496
497  cmd_ptr->command.state = LADISH_COMMAND_STATE_WAITING;
498
499  ladish_app_supervisor_save(g_studio.app_supervisor, cmd_ptr, ladish_studio_apps_save_complete);
500
501  return true;
502}
503
504static void destructor(void * command_context)
505{
506  log_info("save studio command destructor");
507  if (cmd_ptr->studio_name != NULL)
508  {
509    free(cmd_ptr->studio_name);
510  }
511}
512
513#undef cmd_ptr
514
515bool ladish_command_save_studio(void * call_ptr, struct ladish_cqueue * queue_ptr, const char * new_studio_name)
516{
517  struct ladish_command_save_studio * cmd_ptr;
518  char * studio_name_dup;
519
520  studio_name_dup = strdup(new_studio_name);
521  if (studio_name_dup == NULL)
522  {
523    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "strdup('%s') failed.", new_studio_name);
524    goto fail;
525  }
526
527  cmd_ptr = ladish_command_new(sizeof(struct ladish_command_save_studio));
528  if (cmd_ptr == NULL)
529  {
530    log_error("ladish_command_new() failed.");
531    goto fail_free_name;
532  }
533
534  cmd_ptr->command.run = run;
535  cmd_ptr->command.destructor = destructor;
536  cmd_ptr->studio_name = studio_name_dup;
537  cmd_ptr->done = false;
538  cmd_ptr->success = true;
539
540  if (!ladish_cqueue_add_command(queue_ptr, &cmd_ptr->command))
541  {
542    lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "ladish_cqueue_add_command() failed.");
543    goto fail_destroy_command;
544  }
545
546  return true;
547
548fail_destroy_command:
549  free(cmd_ptr);
550fail_free_name:
551  free(studio_name_dup);
552fail:
553  return false;
554}
Note: See TracBrowser for help on using the browser.