gladish: canvas zoom functionality. Fixes #66
[ladish.git] / daemon / cmd_save_studio.c
blob48c1bc69e1873065a8fd53a313ad2de6b1355f78
1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
2 /*
3 * LADI Session Handler (ladish)
5 * Copyright (C) 2009,2010 Nedko Arnaudov <nedko@arnaudov.name>
7 **************************************************************************
8 * This file contains implementation of the "save studio" command
9 **************************************************************************
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.
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.
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.
27 #include <sys/types.h>
28 #include <signal.h>
30 #include "common.h"
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 #include <unistd.h>
37 #include "escape.h"
38 #include "studio_internal.h"
39 #include "cmd.h"
40 #include "../proxies/notify_proxy.h"
41 #include "save.h"
43 #define STUDIO_HEADER_TEXT BASE_NAME " Studio configuration.\n"
45 bool
46 write_jack_parameter(
47 int fd,
48 int indent,
49 struct jack_conf_parameter * parameter_ptr)
51 const char * src;
52 char * dst;
53 char path[JACK_CONF_MAX_ADDRESS_SIZE * 3]; /* encode each char in three bytes (percent encoding) */
54 const char * content;
55 char valbuf[100];
57 /* compose the parameter path, percent-encode "bad" chars */
58 src = parameter_ptr->address;
59 dst = path;
62 *dst++ = '/';
63 escape(&src, &dst);
64 src++;
66 while (*src != 0);
67 *dst = 0;
69 if (!ladish_write_indented_string(fd, indent, "<parameter path=\""))
71 return false;
74 if (!ladish_write_string(fd, path))
76 return false;
79 if (!ladish_write_string(fd, "\">"))
81 return false;
84 switch (parameter_ptr->parameter.type)
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;
115 if (!ladish_write_string(fd, content))
117 return false;
120 if (!ladish_write_string(fd, "</parameter>\n"))
122 return false;
125 return true;
128 #define fd (((struct ladish_write_context *)context)->fd)
129 #define indent (((struct ladish_write_context *)context)->indent)
131 static bool save_studio_room(void * context, ladish_room_handle room)
133 uuid_t uuid;
134 char str[37];
136 log_info("saving room '%s'", ladish_room_get_name(room));
138 if (!ladish_write_indented_string(fd, indent, "<room name=\""))
140 return false;
143 if (!ladish_write_string(fd, ladish_room_get_name(room)))
145 return false;
148 if (!ladish_write_string(fd, "\" uuid=\""))
150 return false;
153 ladish_room_get_uuid(room, uuid);
154 uuid_unparse(uuid, str);
156 if (!ladish_write_string(fd, str))
158 return false;
161 if (!ladish_write_string(fd, "\">\n"))
163 return false;
166 if (!ladish_write_room_link_ports(fd, indent + 1, room))
168 log_error("ladish_write_room_link_ports() failed");
169 return false;
172 if (!ladish_write_indented_string(fd, indent, " </room>\n"))
174 return false;
177 return true;
180 #undef indent
181 #undef fd
183 struct ladish_command_save_studio
185 struct ladish_command command;
186 char * studio_name;
189 #define cmd_ptr ((struct ladish_command_save_studio *)command_context)
191 static bool run(void * command_context)
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;
206 ASSERT(cmd_ptr->command.state == LADISH_COMMAND_STATE_PENDING);
208 time(&timestamp);
209 ctime_r(&timestamp, timestamp_str);
210 timestamp_str[24] = 0;
212 ret = false;
214 ladish_app_supervisor_save_L1(g_studio.app_supervisor);
216 if (!ladish_studio_is_started())
218 log_error("Cannot save not-started studio");
219 ladish_notify_simple(LADISH_NOTIFY_URGENCY_HIGH, "Cannot save not-started studio", NULL);
220 goto exit;
223 if (!ladish_studio_compose_filename(cmd_ptr->studio_name, &filename, &bak_filename))
225 log_error("failed to compose studio filename");
226 goto exit;
229 /* whether save will initiate a rename */
230 renaming = strcmp(cmd_ptr->studio_name, g_studio.name) != 0;
232 if (g_studio.filename == NULL)
234 /* saving studio for first time */
235 g_studio.filename = filename;
236 free(bak_filename);
237 bak_filename = NULL;
238 old_filename = NULL;
240 else if (strcmp(g_studio.filename, filename) == 0)
242 /* saving already persisted studio that was not renamed */
243 old_filename = filename;
245 else if (!renaming)
247 /* saving already renamed studio */
248 old_filename = g_studio.filename;
249 g_studio.filename = filename;
251 else
253 /* saving studio copy (save as) */
254 old_filename = filename;
255 g_studio.filename = filename;
258 filename = NULL;
259 ASSERT(g_studio.filename != NULL);
260 ASSERT(g_studio.filename != bak_filename);
262 if (bak_filename != NULL)
264 ASSERT(old_filename != NULL);
266 if (stat(old_filename, &st) == 0) /* if old filename does not exist, rename with fail */
268 if (rename(old_filename, bak_filename) != 0)
270 log_error("rename(%s, %s) failed: %d (%s)", old_filename, bak_filename, errno, strerror(errno));
271 goto free_filenames;
274 else
276 /* mark that there is no backup file */
277 free(bak_filename);
278 bak_filename = NULL;
282 log_info("saving studio... (%s)", g_studio.filename);
284 fd = open(g_studio.filename, O_WRONLY | O_TRUNC | O_CREAT, 0666);
285 if (fd == -1)
287 log_error("open(%s) failed: %d (%s)", g_studio.filename, errno, strerror(errno));
288 goto rename_back;
291 if (!ladish_write_string(fd, "<?xml version=\"1.0\"?>\n"))
293 goto close;
296 if (!ladish_write_string(fd, "<!--\n"))
298 goto close;
301 if (!ladish_write_string(fd, STUDIO_HEADER_TEXT))
303 goto close;
306 if (!ladish_write_string(fd, "-->\n"))
308 goto close;
311 if (!ladish_write_string(fd, "<!-- "))
313 goto close;
316 if (!ladish_write_string(fd, timestamp_str))
318 goto close;
321 if (!ladish_write_string(fd, " -->\n"))
323 goto close;
326 if (!ladish_write_string(fd, "<studio>\n"))
328 goto close;
331 if (!ladish_write_indented_string(fd, 1, "<jack>\n"))
333 goto close;
336 if (!ladish_write_indented_string(fd, 2, "<conf>\n"))
338 goto close;
341 list_for_each(node_ptr, &g_studio.jack_params)
343 parameter_ptr = list_entry(node_ptr, struct jack_conf_parameter, leaves);
345 if (!write_jack_parameter(fd, 3, parameter_ptr))
347 goto close;
351 if (!ladish_write_indented_string(fd, 2, "</conf>\n"))
353 goto close;
356 if (!ladish_write_jgraph(fd, 2, ladish_studio_get_studio_graph()))
358 log_error("ladish_write_jgraph() failed for studio graph");
359 goto close;
362 if (!ladish_write_indented_string(fd, 1, "</jack>\n"))
364 goto close;
367 if (ladish_studio_has_rooms())
369 if (!ladish_write_indented_string(fd, 1, "<rooms>\n"))
371 goto close;
374 save_context.indent = 2;
375 save_context.fd = fd;
377 if (!ladish_studio_iterate_rooms(&save_context, save_studio_room))
379 log_error("ladish_studio_iterate_rooms() failed");
380 goto close;
383 if (!ladish_write_indented_string(fd, 1, "</rooms>\n"))
385 goto close;
389 if (!ladish_write_vgraph(fd, 1, g_studio.studio_graph, g_studio.app_supervisor))
391 log_error("ladish_write_vgraph() failed for studio");
392 goto close;
395 if (!ladish_write_dict(fd, 1, ladish_graph_get_dict(g_studio.studio_graph)))
397 goto close;
400 if (!ladish_write_string(fd, "</studio>\n"))
402 goto close;
405 log_info("studio saved. (%s)", g_studio.filename);
406 g_studio.persisted = true;
407 g_studio.automatic = false; /* even if it was automatic, it is not anymore because it is saved */
409 cmd_ptr->command.state = LADISH_COMMAND_STATE_DONE;
411 ret = true;
413 if (renaming)
415 free(g_studio.name);
416 g_studio.name = cmd_ptr->studio_name;
417 cmd_ptr->studio_name = NULL; /* mark that descructor does not need to free the new name buffer */
418 ladish_studio_emit_renamed(); /* uses g_studio.name */
421 close:
422 close(fd);
424 rename_back:
425 if (!ret && bak_filename != NULL)
427 /* save failed - try to rename the backup file back */
428 ASSERT(old_filename != NULL);
429 if (rename(bak_filename, old_filename) != 0)
431 log_error("rename(%s, %s) failed: %d (%s)", bak_filename, g_studio.filename, errno, strerror(errno));
435 free_filenames:
436 if (bak_filename != NULL)
438 free(bak_filename);
441 if (old_filename != NULL && old_filename != g_studio.filename)
443 free(old_filename);
446 ASSERT(filename == NULL);
447 ASSERT(g_studio.filename != NULL);
449 exit:
450 if (!ret)
452 ladish_notify_simple(LADISH_NOTIFY_URGENCY_HIGH, "Studio save failed", "Please inspect the ladishd log (~/.ladish/ladish.log) for more info");
455 return ret;
458 static void destructor(void * command_context)
460 log_info("save studio command destructor");
461 if (cmd_ptr->studio_name != NULL)
463 free(cmd_ptr->studio_name);
467 #undef cmd_ptr
469 bool ladish_command_save_studio(void * call_ptr, struct ladish_cqueue * queue_ptr, const char * new_studio_name)
471 struct ladish_command_save_studio * cmd_ptr;
472 char * studio_name_dup;
474 studio_name_dup = strdup(new_studio_name);
475 if (studio_name_dup == NULL)
477 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "strdup('%s') failed.", new_studio_name);
478 goto fail;
481 cmd_ptr = ladish_command_new(sizeof(struct ladish_command_save_studio));
482 if (cmd_ptr == NULL)
484 log_error("ladish_command_new() failed.");
485 goto fail_free_name;
488 cmd_ptr->command.run = run;
489 cmd_ptr->command.destructor = destructor;
490 cmd_ptr->studio_name = studio_name_dup;
492 if (!ladish_cqueue_add_command(queue_ptr, &cmd_ptr->command))
494 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "ladish_cqueue_add_command() failed.");
495 goto fail_destroy_command;
498 return true;
500 fail_destroy_command:
501 free(cmd_ptr);
502 fail_free_name:
503 free(studio_name_dup);
504 fail:
505 return false;