studio "save as". Fixes #13
[ladish.git] / daemon / cmd_save_studio.c
blobc02700d8c6c788ec7b594e480548a2d1566c444a
1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
2 /*
3 * LADI Session Handler (ladish)
5 * Copyright (C) 2009 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 "common.h"
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #include <unistd.h>
34 #include "escape.h"
35 #include "studio_internal.h"
36 #include "cmd.h"
38 struct save_context
40 int fd;
41 const char * indent;
44 #define STUDIO_HEADER_TEXT BASE_NAME " Studio configuration.\n"
46 bool
47 write_string(int fd, const char * string)
49 size_t len;
51 len = strlen(string);
53 if (write(fd, string, len) != len)
55 log_error("write() failed to write config file.");
56 return false;
59 return true;
62 bool
63 write_jack_parameter(
64 int fd,
65 const char * indent,
66 struct jack_conf_parameter * parameter_ptr)
68 const char * src;
69 char * dst;
70 char path[JACK_CONF_MAX_ADDRESS_SIZE * 3]; /* encode each char in three bytes (percent encoding) */
71 const char * content;
72 char valbuf[100];
74 /* compose the parameter path, percent-encode "bad" chars */
75 src = parameter_ptr->address;
76 dst = path;
79 *dst++ = '/';
80 escape(&src, &dst);
81 src++;
83 while (*src != 0);
84 *dst = 0;
86 if (!write_string(fd, indent))
88 return false;
91 if (!write_string(fd, "<parameter path=\""))
93 return false;
96 if (!write_string(fd, path))
98 return false;
101 if (!write_string(fd, "\">"))
103 return false;
106 switch (parameter_ptr->parameter.type)
108 case jack_boolean:
109 content = parameter_ptr->parameter.value.boolean ? "true" : "false";
110 log_debug("%s value is %s (boolean)", path, content);
111 break;
112 case jack_string:
113 content = parameter_ptr->parameter.value.string;
114 log_debug("%s value is %s (string)", path, content);
115 break;
116 case jack_byte:
117 valbuf[0] = (char)parameter_ptr->parameter.value.byte;
118 valbuf[1] = 0;
119 content = valbuf;
120 log_debug("%s value is %u/%c (byte/char)", path, parameter_ptr->parameter.value.byte, (char)parameter_ptr->parameter.value.byte);
121 break;
122 case jack_uint32:
123 snprintf(valbuf, sizeof(valbuf), "%" PRIu32, parameter_ptr->parameter.value.uint32);
124 content = valbuf;
125 log_debug("%s value is %s (uint32)", path, content);
126 break;
127 case jack_int32:
128 snprintf(valbuf, sizeof(valbuf), "%" PRIi32, parameter_ptr->parameter.value.int32);
129 content = valbuf;
130 log_debug("%s value is %s (int32)", path, content);
131 break;
132 default:
133 log_error("unknown jack parameter_ptr->parameter type %d (%s)", (int)parameter_ptr->parameter.type, path);
134 return false;
137 if (!write_string(fd, content))
139 return false;
142 if (!write_string(fd, "</parameter>\n"))
144 return false;
147 return true;
150 static
151 bool
152 write_dict_entry(
153 void * context,
154 const char * key,
155 const char * value);
157 bool
158 write_dict(
159 int fd,
160 const char * indent,
161 ladish_dict_handle dict)
163 struct save_context context;
165 if (ladish_dict_is_empty(dict))
167 return true;
170 if (!write_string(fd, indent))
172 return false;
175 context.fd = fd;
176 context.indent = indent;
178 if (!write_string(fd, "<dict>\n"))
180 return false;
183 if (!ladish_dict_iterate(dict, &context, write_dict_entry))
185 return false;
188 if (!write_string(fd, indent))
190 return false;
193 if (!write_string(fd, "</dict>\n"))
195 return false;
198 return true;
201 #define fd (((struct save_context *)context)->fd)
202 #define indent (((struct save_context *)context)->indent)
204 static
205 bool
206 write_dict_entry(
207 void * context,
208 const char * key,
209 const char * value)
211 if (!write_string(fd, indent))
213 return false;
216 if (!write_string(fd, " <key name=\""))
218 return false;
221 if (!write_string(fd, key))
223 return false;
226 if (!write_string(fd, "\">"))
228 return false;
231 if (!write_string(fd, value))
233 return false;
236 if (!write_string(fd, "</key>\n"))
238 return false;
241 return true;
244 bool
245 save_jack_client_begin(
246 void * context,
247 ladish_client_handle client_handle,
248 const char * client_name,
249 void ** client_iteration_context_ptr_ptr)
251 uuid_t uuid;
252 char str[37];
254 ladish_client_get_uuid(client_handle, uuid);
255 uuid_unparse(uuid, str);
257 log_info("saving jack client '%s' (%s)", client_name, str);
259 if (!write_string(fd, " <client name=\""))
261 return false;
264 if (!write_string(fd, client_name))
266 return false;
269 if (!write_string(fd, "\" uuid=\""))
271 return false;
274 if (!write_string(fd, str))
276 return false;
279 if (!write_string(fd, "\">\n"))
281 return false;
284 if (!write_string(fd, " <ports>\n"))
286 return false;
289 return true;
292 bool
293 save_jack_client_end(
294 void * context,
295 ladish_client_handle client_handle,
296 const char * client_name,
297 void * client_iteration_context_ptr)
299 if (!write_string(fd, " </ports>\n"))
301 return false;
304 if (!write_string(fd, " </client>\n"))
306 return false;
309 return true;
312 bool
313 save_jack_port(
314 void * context,
315 void * client_iteration_context_ptr,
316 ladish_client_handle client_handle,
317 const char * client_name,
318 ladish_port_handle port_handle,
319 const char * port_name,
320 uint32_t port_type,
321 uint32_t port_flags)
323 uuid_t uuid;
324 char str[37];
326 ladish_port_get_uuid(port_handle, uuid);
327 uuid_unparse(uuid, str);
329 log_info("saving jack port '%s':'%s' (%s)", client_name, port_name, str);
331 if (!write_string(fd, " <port name=\""))
333 return false;
336 if (!write_string(fd, port_name))
338 return false;
341 if (!write_string(fd, "\" uuid=\""))
343 return false;
346 if (!write_string(fd, str))
348 return false;
351 if (!write_string(fd, "\" />\n"))
353 return false;
356 return true;
359 bool
360 save_studio_client_begin(
361 void * context,
362 ladish_client_handle client_handle,
363 const char * client_name,
364 void ** client_iteration_context_ptr_ptr)
366 uuid_t uuid;
367 char str[37];
369 ladish_client_get_uuid(client_handle, uuid);
370 uuid_unparse(uuid, str);
372 log_info("saving studio client '%s' (%s)", client_name, str);
374 if (!write_string(fd, " <client name=\""))
376 return false;
379 if (!write_string(fd, client_name))
381 return false;
384 if (!write_string(fd, "\" uuid=\""))
386 return false;
389 if (!write_string(fd, str))
391 return false;
394 if (!write_string(fd, "\">\n"))
396 return false;
399 if (!write_string(fd, " <ports>\n"))
401 return false;
404 return true;
407 bool
408 save_studio_client_end(
409 void * context,
410 ladish_client_handle client_handle,
411 const char * client_name,
412 void * client_iteration_context_ptr)
414 if (!write_string(fd, " </ports>\n"))
416 return false;
419 if (!write_dict(fd, " ", ladish_client_get_dict(client_handle)))
421 return false;
424 if (!write_string(fd, " </client>\n"))
426 return false;
429 return true;
432 bool
433 save_studio_port(
434 void * context,
435 void * client_iteration_context_ptr,
436 ladish_client_handle client_handle,
437 const char * client_name,
438 ladish_port_handle port_handle,
439 const char * port_name,
440 uint32_t port_type,
441 uint32_t port_flags)
443 uuid_t uuid;
444 char str[37];
445 ladish_dict_handle dict;
447 ladish_port_get_uuid(port_handle, uuid);
448 uuid_unparse(uuid, str);
450 log_info("saving studio port '%s':'%s' (%s)", client_name, port_name, str);
452 if (!write_string(fd, " <port name=\""))
454 return false;
457 if (!write_string(fd, port_name))
459 return false;
462 if (!write_string(fd, "\" uuid=\""))
464 return false;
467 if (!write_string(fd, str))
469 return false;
472 dict = ladish_port_get_dict(port_handle);
473 if (ladish_dict_is_empty(dict))
475 if (!write_string(fd, "\" />\n"))
477 return false;
480 else
482 if (!write_string(fd, "\">\n"))
484 return false;
487 if (!write_dict(fd, " ", dict))
489 return false;
492 if (!write_string(fd, " </port>\n"))
494 return false;
498 return true;
501 bool save_studio_connection(void * context, ladish_port_handle port1_handle, ladish_port_handle port2_handle, ladish_dict_handle dict)
503 uuid_t uuid;
504 char str[37];
506 log_info("saving studio connection");
508 if (!write_string(fd, " <connection port1=\""))
510 return false;
513 ladish_port_get_uuid(port1_handle, uuid);
514 uuid_unparse(uuid, str);
516 if (!write_string(fd, str))
518 return false;
521 if (!write_string(fd, "\" port2=\""))
523 return false;
526 ladish_port_get_uuid(port2_handle, uuid);
527 uuid_unparse(uuid, str);
529 if (!write_string(fd, str))
531 return false;
534 if (ladish_dict_is_empty(dict))
536 if (!write_string(fd, "\" />\n"))
538 return false;
541 else
543 if (!write_string(fd, "\">\n"))
545 return false;
548 if (!write_dict(fd, " ", dict))
550 return false;
553 if (!write_string(fd, " </connection>\n"))
555 return false;
559 return true;
562 bool save_studio_app(void * context, const char * name, bool running, const char * command, bool terminal, uint8_t level)
564 char buf[100];
565 const char * unescaped_string;
566 char * escaped_string;
567 char * escaped_buffer;
568 bool ret;
570 log_info("saving app: name='%s', %srunning, %s, level %u, commandline='%s'", name, running ? "" : "not ", terminal ? "terminal" : "shell", (unsigned int)level, command);
572 ret = false;
574 escaped_buffer = malloc(ladish_max(strlen(name), strlen(command)) * 3 + 1); /* encode each char in three bytes (percent encoding) */
575 if (escaped_buffer == NULL)
577 log_error("malloc() failed.");
578 goto exit;
581 if (!write_string(fd, " <application name=\""))
583 goto free_buffer;
586 unescaped_string = name;
587 escaped_string = escaped_buffer;
588 escape(&unescaped_string, &escaped_string);
589 *escaped_string = 0;
590 if (!write_string(fd, escaped_buffer))
592 goto free_buffer;
595 if (!write_string(fd, "\" terminal=\""))
597 goto free_buffer;
600 if (!write_string(fd, terminal ? "true" : "false"))
602 goto free_buffer;
605 if (!write_string(fd, "\" level=\""))
607 goto free_buffer;
610 sprintf(buf, "%u", (unsigned int)level);
612 if (!write_string(fd, buf))
614 goto free_buffer;
617 if (!write_string(fd, "\" autorun=\""))
619 goto free_buffer;
622 if (!write_string(fd, running ? "true" : "false"))
624 goto free_buffer;
627 if (!write_string(fd, "\">"))
629 goto free_buffer;
632 unescaped_string = command;
633 escaped_string = escaped_buffer;
634 escape(&unescaped_string, &escaped_string);
635 *escaped_string = 0;
636 if (!write_string(fd, escaped_buffer))
638 goto free_buffer;
641 if (!write_string(fd, "</application>\n"))
643 goto free_buffer;
646 ret = true;
648 free_buffer:
649 free(escaped_buffer);
651 exit:
652 return ret;
655 #undef indent
656 #undef fd
658 struct ladish_command_save_studio
660 struct ladish_command command;
661 char * studio_name;
664 #define cmd_ptr ((struct ladish_command_save_studio *)command_context)
666 static bool run(void * command_context)
668 struct list_head * node_ptr;
669 struct jack_conf_parameter * parameter_ptr;
670 int fd;
671 time_t timestamp;
672 char timestamp_str[26];
673 bool ret;
674 char * filename; /* filename */
675 char * bak_filename; /* filename of the backup file */
676 char * old_filename; /* filename where studio was persisted before save */
677 struct stat st;
678 struct save_context save_context;
680 ASSERT(cmd_ptr->command.state == LADISH_COMMAND_STATE_PENDING);
682 time(&timestamp);
683 ctime_r(&timestamp, timestamp_str);
684 timestamp_str[24] = 0;
686 ret = false;
688 if (!studio_is_started())
690 log_error("Cannot save not-started studio");
691 goto exit;
694 if (!studio_compose_filename(cmd_ptr->studio_name, &filename, &bak_filename))
696 log_error("failed to compose studio filename");
697 goto exit;
700 if (g_studio.filename == NULL)
702 /* saving studio for first time */
703 g_studio.filename = filename;
704 free(bak_filename);
705 bak_filename = NULL;
706 old_filename = NULL;
708 else if (strcmp(g_studio.filename, filename) == 0)
710 /* saving already persisted studio that was not renamed */
711 old_filename = filename;
713 else if (strcmp(cmd_ptr->studio_name, g_studio.name) == 0)
715 /* saving renamed studio */
716 old_filename = g_studio.filename;
717 g_studio.filename = filename;
719 else
721 /* saving studio copy (save as) */
722 old_filename = filename;
723 g_studio.filename = filename;
726 filename = NULL;
727 ASSERT(g_studio.filename != NULL);
728 ASSERT(g_studio.filename != bak_filename);
730 if (bak_filename != NULL)
732 ASSERT(old_filename != NULL);
734 if (stat(old_filename, &st) == 0) /* if old filename does not exist, rename with fail */
736 if (rename(old_filename, bak_filename) != 0)
738 log_error("rename(%s, %s) failed: %d (%s)", old_filename, bak_filename, errno, strerror(errno));
739 goto free_filenames;
742 else
744 /* mark that there is no backup file */
745 free(bak_filename);
746 bak_filename = NULL;
750 log_info("saving studio... (%s)", g_studio.filename);
752 fd = open(g_studio.filename, O_WRONLY | O_TRUNC | O_CREAT, 0700);
753 if (fd == -1)
755 log_error("open(%s) failed: %d (%s)", g_studio.filename, errno, strerror(errno));
756 goto rename_back;
759 if (!write_string(fd, "<?xml version=\"1.0\"?>\n"))
761 goto close;
764 if (!write_string(fd, "<!--\n"))
766 goto close;
769 if (!write_string(fd, STUDIO_HEADER_TEXT))
771 goto close;
774 if (!write_string(fd, "-->\n"))
776 goto close;
779 if (!write_string(fd, "<!-- "))
781 goto close;
784 if (!write_string(fd, timestamp_str))
786 goto close;
789 if (!write_string(fd, " -->\n"))
791 goto close;
794 if (!write_string(fd, "<studio>\n"))
796 goto close;
799 if (!write_string(fd, " <jack>\n"))
801 goto close;
804 if (!write_string(fd, " <conf>\n"))
806 goto close;
809 list_for_each(node_ptr, &g_studio.jack_params)
811 parameter_ptr = list_entry(node_ptr, struct jack_conf_parameter, leaves);
813 if (!write_jack_parameter(fd, " ", parameter_ptr))
815 goto close;
819 if (!write_string(fd, " </conf>\n"))
821 goto close;
824 if (!write_string(fd, " <clients>\n"))
826 goto close;
829 save_context.fd = fd;
831 if (!ladish_graph_iterate_nodes(g_studio.jack_graph, &save_context, save_jack_client_begin, save_jack_port, save_jack_client_end))
833 log_error("ladish_graph_iterate_nodes() failed");
834 goto close;
837 if (!write_string(fd, " </clients>\n"))
839 goto close;
842 if (!write_string(fd, " </jack>\n"))
844 goto close;
847 if (!write_string(fd, " <clients>\n"))
849 goto close;
852 if (!ladish_graph_iterate_nodes(g_studio.studio_graph, &save_context, save_studio_client_begin, save_studio_port, save_studio_client_end))
854 log_error("ladish_graph_iterate_nodes() failed");
855 goto close;
858 if (!write_string(fd, " </clients>\n"))
860 goto close;
863 if (!write_string(fd, " <connections>\n"))
865 goto close;
868 if (!ladish_graph_iterate_connections(g_studio.studio_graph, &save_context, save_studio_connection))
870 log_error("ladish_graph_iterate_connections() failed");
871 goto close;
874 if (!write_string(fd, " </connections>\n"))
876 goto close;
879 if (!write_string(fd, " <applications>\n"))
881 goto close;
884 if (!ladish_app_supervisor_enum(g_studio.app_supervisor, &save_context, save_studio_app))
886 goto close;
889 if (!write_string(fd, " </applications>\n"))
891 goto close;
894 if (!write_dict(fd, " ", ladish_graph_get_dict(g_studio.studio_graph)))
896 goto close;
899 if (!write_string(fd, "</studio>\n"))
901 goto close;
904 log_info("studio saved. (%s)", g_studio.filename);
905 g_studio.persisted = true;
906 g_studio.automatic = false; /* even if it was automatic, it is not anymore because it is saved */
908 cmd_ptr->command.state = LADISH_COMMAND_STATE_DONE;
910 ret = true;
912 if (old_filename == g_studio.filename && strcmp(g_studio.name, cmd_ptr->studio_name) != 0)
914 free(g_studio.name);
915 g_studio.name = cmd_ptr->studio_name;
916 cmd_ptr->studio_name = NULL;
917 emit_studio_renamed();
920 close:
921 close(fd);
923 rename_back:
924 if (!ret && bak_filename != NULL)
926 /* save failed - try to rename the backup file back */
927 ASSERT(old_filename != NULL);
928 if (rename(bak_filename, old_filename) != 0)
930 log_error("rename(%s, %s) failed: %d (%s)", bak_filename, g_studio.filename, errno, strerror(errno));
934 free_filenames:
935 if (bak_filename != NULL)
937 free(bak_filename);
940 if (old_filename != NULL && old_filename != g_studio.filename)
942 free(old_filename);
945 ASSERT(filename == NULL);
946 ASSERT(g_studio.filename != NULL);
948 exit:
949 return ret;
952 static void destructor(void * command_context)
954 log_info("save studio command destructor");
955 if (cmd_ptr->studio_name != NULL)
957 free(cmd_ptr->studio_name);
961 #undef cmd_ptr
963 bool ladish_command_save_studio(void * call_ptr, struct ladish_cqueue * queue_ptr, const char * new_studio_name)
965 struct ladish_command_save_studio * cmd_ptr;
966 char * studio_name_dup;
968 studio_name_dup = strdup(new_studio_name);
969 if (studio_name_dup == NULL)
971 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "strdup('%s') failed.", new_studio_name);
972 goto fail;
975 cmd_ptr = ladish_command_new(sizeof(struct ladish_command_save_studio));
976 if (cmd_ptr == NULL)
978 log_error("ladish_command_new() failed.");
979 goto fail_free_name;
982 cmd_ptr->command.run = run;
983 cmd_ptr->command.destructor = destructor;
984 cmd_ptr->studio_name = studio_name_dup;
986 if (!ladish_cqueue_add_command(queue_ptr, &cmd_ptr->command))
988 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "ladish_cqueue_add_command() failed.");
989 goto fail_destroy_command;
992 return true;
994 fail_destroy_command:
995 free(cmd_ptr);
996 fail_free_name:
997 free(studio_name_dup);
998 fail:
999 return false;