ladishd: save hidden porst of stopped apps.
[ladish.git] / daemon / save.c
blobdd508b4efedaff4ac88c7662b78ed15f1f1314b7
1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
2 /*
3 * LADI Session Handler (ladish)
5 * Copyright (C) 2010 Nedko Arnaudov <nedko@arnaudov.name>
7 **************************************************************************
8 * This file contains implementation save releated helper functions
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 <unistd.h>
29 #include "save.h"
30 #include "escape.h"
31 #include "studio.h"
33 struct ladish_write_vgraph_context
35 int fd;
36 int indent;
37 ladish_app_supervisor_handle app_supervisor;
38 bool client_visible;
41 static bool is_system_client(ladish_client_handle client)
43 uuid_t uuid;
44 ladish_client_get_uuid(client, uuid);
45 return ladish_virtualizer_is_system_client(uuid);
48 static bool is_hidden_port_interesting(ladish_app_supervisor_handle app_supervisor, ladish_client_handle client, ladish_port_handle port)
50 uuid_t app_uuid;
51 ladish_app_handle app;
53 if (is_system_client(client) || ladish_port_is_link(port))
55 return true;
58 /* hidden ports of external apps should not be saved */
59 /* hidden ports of stopped managed apps should be saved */
60 /* hidden ports of started managed apps should not be saved */
62 if (!ladish_port_get_app(port, app_uuid))
64 /* port of external app, don't save */
65 return false;
68 app = ladish_app_supervisor_find_app_by_uuid(app_supervisor, app_uuid);
69 if (app == NULL)
71 ASSERT_NO_PASS; /* this should not happen because app ports are removed before app is removed */
72 return false;
75 return !ladish_app_is_running(app);
78 bool ladish_write_string(int fd, const char * string)
80 size_t len;
81 ssize_t ret;
83 len = strlen(string);
85 ret = write(fd, string, len);
86 if (ret == -1)
88 log_error("write(%d, \"%s\", %zu) failed to write file: %d (%s)", fd, string, len, errno, strerror(errno));
89 return false;
91 if ((size_t)ret != len)
93 log_error("write() wrote wrong byte count to file (%zd != %zu).", ret, len);
94 return false;
97 return true;
100 bool ladish_write_indented_string(int fd, int indent, const char * string)
102 ASSERT(indent >= 0);
103 while (indent--)
105 if (!ladish_write_string(fd, LADISH_XML_BASE_INDENT))
107 return false;
111 if (!ladish_write_string(fd, string))
113 return false;
116 return true;
119 bool ladish_write_string_escape_ex(int fd, const char * string, unsigned int flags)
121 bool ret;
122 char * escaped_buffer;
124 escaped_buffer = malloc(max_escaped_length(strlen(string)));
125 if (escaped_buffer == NULL)
127 log_error("malloc() failed to allocate buffer for escaped string");
128 return false;
131 escape_simple(string, escaped_buffer, flags);
133 ret = ladish_write_string(fd, escaped_buffer);
135 free(escaped_buffer);
137 return ret;
140 bool ladish_write_string_escape(int fd, const char * string)
142 return ladish_write_string_escape_ex(fd, string, LADISH_ESCAPE_FLAG_ALL);
145 #define fd (((struct ladish_write_context *)context)->fd)
146 #define indent (((struct ladish_write_context *)context)->indent)
148 static
149 bool
150 write_dict_entry(
151 void * context,
152 const char * key,
153 const char * value)
155 if (!ladish_write_indented_string(fd, indent, "<key name=\""))
157 return false;
160 if (!ladish_write_string(fd, key))
162 return false;
165 if (!ladish_write_string(fd, "\">"))
167 return false;
170 if (!ladish_write_string(fd, value))
172 return false;
175 if (!ladish_write_string(fd, "</key>\n"))
177 return false;
180 return true;
183 static
184 bool
185 ladish_write_room_port(
186 void * context,
187 ladish_port_handle port,
188 const char * name,
189 uint32_t type,
190 uint32_t flags)
192 uuid_t uuid;
193 char str[37];
194 bool midi;
195 const char * type_str;
196 bool playback;
197 const char * direction_str;
198 ladish_dict_handle dict;
200 ladish_port_get_uuid(port, uuid);
201 uuid_unparse(uuid, str);
203 playback = (flags & JACKDBUS_PORT_FLAG_INPUT) != 0;
204 ASSERT(playback || (flags & JACKDBUS_PORT_FLAG_OUTPUT) != 0); /* playback or capture */
205 ASSERT(!(playback && (flags & JACKDBUS_PORT_FLAG_OUTPUT) != 0)); /* but not both */
206 direction_str = playback ? "playback" : "capture";
208 midi = type == JACKDBUS_PORT_TYPE_MIDI;
209 ASSERT(midi || type == JACKDBUS_PORT_TYPE_AUDIO); /* midi or audio */
210 ASSERT(!(midi && type == JACKDBUS_PORT_TYPE_AUDIO)); /* but not both */
211 type_str = midi ? "midi" : "audio";
213 log_info("saving room %s %s port '%s' (%s)", direction_str, type_str, name, str);
215 if (!ladish_write_indented_string(fd, indent, "<port name=\""))
217 return false;
220 if (!ladish_write_string_escape_ex(fd, name, LADISH_ESCAPE_FLAG_XML_ATTR))
222 return false;
225 if (!ladish_write_string(fd, "\" uuid=\""))
227 return false;
230 if (!ladish_write_string(fd, str))
232 return false;
235 if (!ladish_write_string(fd, "\" type=\""))
237 return false;
240 if (!ladish_write_string(fd, type_str))
242 return false;
245 if (!ladish_write_string(fd, "\" direction=\""))
247 return false;
250 if (!ladish_write_string(fd, direction_str))
252 return false;
255 dict = ladish_port_get_dict(port);
256 if (ladish_dict_is_empty(dict))
258 if (!ladish_write_string(fd, "\" />\n"))
260 return false;
263 else
265 if (!ladish_write_string(fd, "\">\n"))
267 return false;
270 if (!ladish_write_dict(fd, indent + 1, dict))
272 return false;
275 if (!ladish_write_indented_string(fd, indent, "</port>\n"))
277 return false;
281 return true;
284 #undef indent
285 #undef fd
287 bool ladish_write_dict(int fd, int indent, ladish_dict_handle dict)
289 struct ladish_write_context context;
291 if (ladish_dict_is_empty(dict))
293 return true;
296 context.fd = fd;
297 context.indent = indent + 1;
299 if (!ladish_write_indented_string(fd, indent, "<dict>\n"))
301 return false;
304 if (!ladish_dict_iterate(dict, &context, write_dict_entry))
306 return false;
309 if (!ladish_write_indented_string(fd, indent, "</dict>\n"))
311 return false;
314 return true;
317 bool ladish_write_room_link_ports(int fd, int indent, ladish_room_handle room)
319 struct ladish_write_context context;
321 ladish_check_integrity();
323 context.fd = fd;
324 context.indent = indent;
326 if (!ladish_room_iterate_link_ports(room, &context, ladish_write_room_port))
328 log_error("ladish_room_iterate_link_ports() failed");
329 return false;
332 return true;
335 /****************/
336 /* write vgraph */
337 /****************/
339 #define fd (((struct ladish_write_vgraph_context *)context)->fd)
340 #define indent (((struct ladish_write_vgraph_context *)context)->indent)
341 #define ctx_ptr ((struct ladish_write_vgraph_context *)context)
343 static
344 bool
345 ladish_save_vgraph_client_begin(
346 void * context,
347 ladish_graph_handle graph,
348 bool hidden,
349 ladish_client_handle client_handle,
350 const char * client_name,
351 void ** client_iteration_context_ptr_ptr)
353 uuid_t uuid;
354 char str[37];
356 ctx_ptr->client_visible = !hidden || ladish_client_has_app(client_handle);
357 if (!ctx_ptr->client_visible)
359 return true;
362 ladish_client_get_uuid(client_handle, uuid);
363 uuid_unparse(uuid, str);
365 log_info("saving vgraph client '%s' (%s)", client_name, str);
367 if (!ladish_write_indented_string(fd, indent, "<client name=\""))
369 return false;
372 if (!ladish_write_string_escape_ex(fd, client_name, LADISH_ESCAPE_FLAG_XML_ATTR))
374 return false;
377 if (!ladish_write_string(fd, "\" uuid=\""))
379 return false;
382 if (!ladish_write_string(fd, str))
384 return false;
387 if (!ladish_write_string(fd, "\" naming=\""))
389 return false;
392 if (!ladish_write_string(fd, "app"))
394 return false;
397 if (!ladish_write_string(fd, "\">\n"))
399 return false;
402 if (!ladish_write_indented_string(fd, indent + 1, "<ports>\n"))
404 return false;
407 return true;
410 static
411 bool
412 ladish_save_vgraph_client_end(
413 void * context,
414 ladish_graph_handle graph,
415 bool hidden,
416 ladish_client_handle client_handle,
417 const char * client_name,
418 void * client_iteration_context_ptr)
420 if (!ctx_ptr->client_visible)
422 return true;
425 if (!ladish_write_indented_string(fd, indent + 1, "</ports>\n"))
427 return false;
430 if (!ladish_write_dict(fd, indent + 1, ladish_client_get_dict(client_handle)))
432 return false;
435 if (!ladish_write_indented_string(fd, indent, "</client>\n"))
437 return false;
440 return true;
443 static bool ladish_get_vgraph_port_uuids(ladish_graph_handle vgraph, ladish_port_handle port, uuid_t uuid, uuid_t link_uuid)
445 bool link;
447 if (vgraph != ladish_studio_get_studio_graph())
449 link = false; /* room ports are saved using their fixed uuids */
451 else
453 link = ladish_port_is_link(port);
454 if (link)
456 /* get the generated port uuid that is used for identification in the virtual graph */
457 ladish_graph_get_port_uuid(vgraph, port, uuid);
461 if (!link || link_uuid != NULL)
463 /* get the real port uuid that is same in both room and studio graphs */
464 ladish_port_get_uuid(port, link ? link_uuid : uuid);
467 return link;
470 static
471 bool
472 ladish_save_vgraph_port(
473 void * context,
474 ladish_graph_handle graph,
475 bool hidden,
476 void * client_iteration_context_ptr,
477 ladish_client_handle client_handle,
478 const char * client_name,
479 ladish_port_handle port_handle,
480 const char * port_name,
481 uint32_t port_type,
482 uint32_t port_flags)
484 uuid_t uuid;
485 bool link;
486 uuid_t link_uuid;
487 char str[37];
488 char link_str[37];
489 ladish_dict_handle dict;
491 if (!ctx_ptr->client_visible)
493 return true;
496 /* skip hidden ports of running apps */
497 if (hidden && !is_hidden_port_interesting(ctx_ptr->app_supervisor, client_handle, port_handle))
499 return true;
502 link = ladish_get_vgraph_port_uuids(graph, port_handle, uuid, link_uuid);
503 uuid_unparse(uuid, str);
504 if (link)
506 uuid_unparse(link_uuid, link_str);
507 log_info("saving vgraph link port '%s':'%s' (%s link=%s)", client_name, port_name, str, link_str);
509 else
511 log_info("saving vgraph port '%s':'%s' (%s)", client_name, port_name, str);
514 if (!ladish_write_indented_string(fd, indent + 2, "<port name=\""))
516 return false;
519 if (!ladish_write_string_escape_ex(fd, port_name, LADISH_ESCAPE_FLAG_XML_ATTR))
521 return false;
524 if (!ladish_write_string(fd, "\" uuid=\""))
526 return false;
529 if (!ladish_write_string(fd, str))
531 return false;
534 if (!ladish_write_string(fd, "\" type=\""))
536 return false;
539 if (!ladish_write_string(fd, port_type == JACKDBUS_PORT_TYPE_AUDIO ? "audio" : "midi"))
541 return false;
544 if (!ladish_write_string(fd, "\" direction=\""))
546 return false;
549 if (!ladish_write_string(fd, JACKDBUS_PORT_IS_INPUT(port_flags) ? "input" : "output"))
551 return false;
554 if (link)
556 if (!ladish_write_string(fd, "\" link_uuid=\""))
558 return false;
561 if (!ladish_write_string(fd, link_str))
563 return false;
567 dict = ladish_port_get_dict(port_handle);
568 if (ladish_dict_is_empty(dict))
570 if (!ladish_write_string(fd, "\" />\n"))
572 return false;
575 else
577 if (!ladish_write_string(fd, "\">\n"))
579 return false;
582 if (!ladish_write_dict(fd, indent + 3, dict))
584 return false;
587 if (!ladish_write_indented_string(fd, indent + 2, "</port>\n"))
589 return false;
593 return true;
596 static
597 bool
598 ladish_save_vgraph_connection(
599 void * context,
600 ladish_graph_handle graph,
601 bool hidden,
602 ladish_client_handle client1,
603 ladish_port_handle port1,
604 ladish_client_handle client2,
605 ladish_port_handle port2,
606 ladish_dict_handle dict)
608 uuid_t uuid;
609 char str[37];
611 if (hidden &&
612 (!is_hidden_port_interesting(ctx_ptr->app_supervisor, client1, port1) ||
613 !is_hidden_port_interesting(ctx_ptr->app_supervisor, client2, port2)))
615 return true;
618 log_info("saving vgraph connection");
620 if (!ladish_write_indented_string(fd, indent, "<connection port1=\""))
622 return false;
625 ladish_get_vgraph_port_uuids(graph, port1, uuid, NULL);
626 uuid_unparse(uuid, str);
628 if (!ladish_write_string(fd, str))
630 return false;
633 if (!ladish_write_string(fd, "\" port2=\""))
635 return false;
638 ladish_get_vgraph_port_uuids(graph, port2, uuid, NULL);
639 uuid_unparse(uuid, str);
641 if (!ladish_write_string(fd, str))
643 return false;
646 if (ladish_dict_is_empty(dict))
648 if (!ladish_write_string(fd, "\" />\n"))
650 return false;
653 else
655 if (!ladish_write_string(fd, "\">\n"))
657 return false;
660 if (!ladish_write_dict(fd, indent + 1, dict))
662 return false;
665 if (!ladish_write_indented_string(fd, indent, "</connection>\n"))
667 return false;
671 return true;
674 static
675 bool
676 ladish_save_app(
677 void * context,
678 const char * name,
679 bool running,
680 const char * command,
681 bool terminal,
682 uint8_t level,
683 pid_t pid,
684 const uuid_t uuid)
686 char buf[100];
687 const char * unescaped_string;
688 char * escaped_string;
689 char * escaped_buffer;
690 bool ret;
692 log_info("saving app: name='%s', %srunning, %s, level %u, commandline='%s'", name, running ? "" : "not ", terminal ? "terminal" : "shell", (unsigned int)level, command);
694 ret = false;
696 escaped_buffer = malloc(max_escaped_length(ladish_max(strlen(name), strlen(command))) + 1);
697 if (escaped_buffer == NULL)
699 log_error("malloc() failed.");
700 goto exit;
703 if (!ladish_write_indented_string(fd, indent, "<application name=\""))
705 goto free_buffer;
708 unescaped_string = name;
709 escaped_string = escaped_buffer;
710 escape(&unescaped_string, &escaped_string, LADISH_ESCAPE_FLAG_ALL);
711 *escaped_string = 0;
712 if (!ladish_write_string(fd, escaped_buffer))
714 goto free_buffer;
717 if (!ladish_write_string(fd, "\" terminal=\""))
719 goto free_buffer;
722 if (!ladish_write_string(fd, terminal ? "true" : "false"))
724 goto free_buffer;
727 if (!ladish_write_string(fd, "\" level=\""))
729 goto free_buffer;
732 sprintf(buf, "%u", (unsigned int)level);
734 if (!ladish_write_string(fd, buf))
736 goto free_buffer;
739 if (!ladish_write_string(fd, "\" autorun=\""))
741 goto free_buffer;
744 if (!ladish_write_string(fd, running ? "true" : "false"))
746 goto free_buffer;
749 if (!ladish_write_string(fd, "\">"))
751 goto free_buffer;
754 unescaped_string = command;
755 escaped_string = escaped_buffer;
756 escape(&unescaped_string, &escaped_string, LADISH_ESCAPE_FLAG_ALL);
757 *escaped_string = 0;
758 if (!ladish_write_string(fd, escaped_buffer))
760 goto free_buffer;
763 if (!ladish_write_string(fd, "</application>\n"))
765 goto free_buffer;
768 ret = true;
770 free_buffer:
771 free(escaped_buffer);
773 exit:
774 return ret;
777 #undef ctx_ptr
778 #undef indent
779 #undef fd
781 bool ladish_write_vgraph(int fd, int indent, ladish_graph_handle vgraph, ladish_app_supervisor_handle app_supervisor)
783 struct ladish_write_vgraph_context context;
785 ladish_check_integrity();
787 context.fd = fd;
788 context.indent = indent + 1;
789 context.app_supervisor = app_supervisor;
791 if (!ladish_write_indented_string(fd, indent, "<clients>\n"))
793 return false;
796 if (!ladish_graph_iterate_nodes(
797 vgraph,
798 &context,
799 ladish_save_vgraph_client_begin,
800 ladish_save_vgraph_port,
801 ladish_save_vgraph_client_end))
803 log_error("ladish_graph_iterate_nodes() failed");
804 return false;
807 if (!ladish_write_indented_string(fd, indent, "</clients>\n"))
809 return false;
812 if (!ladish_write_indented_string(fd, indent, "<connections>\n"))
814 return false;
817 if (!ladish_graph_iterate_connections(vgraph, &context, ladish_save_vgraph_connection))
819 log_error("ladish_graph_iterate_connections() failed");
820 return false;
823 if (!ladish_write_indented_string(fd, indent, "</connections>\n"))
825 return false;
828 if (!ladish_write_indented_string(fd, indent, "<applications>\n"))
830 return false;
833 if (!ladish_app_supervisor_enum(app_supervisor, &context, ladish_save_app))
835 return false;
838 if (!ladish_write_indented_string(fd, indent, "</applications>\n"))
840 return false;
843 return true;
846 /********************/
847 /* write jack graph */
848 /********************/
850 struct ladish_write_jack_context
852 int fd;
853 int indent;
854 ladish_graph_handle vgraph_filter;
855 ladish_app_supervisor_handle app_supervisor;
856 bool client_vgraph_match;
857 bool a2j;
858 bool client_visible;
861 static bool ladish_save_jack_client_write_prolog(int fd, int indent, ladish_client_handle client_handle, const char * client_name)
863 uuid_t uuid;
864 char str[37];
866 ladish_client_get_uuid(client_handle, uuid);
867 uuid_unparse(uuid, str);
869 log_info("saving jack client '%s' (%s)", client_name, str);
871 if (!ladish_write_indented_string(fd, indent, "<client name=\""))
873 return false;
876 if (!ladish_write_string_escape_ex(fd, client_name, LADISH_ESCAPE_FLAG_XML_ATTR))
878 return false;
881 if (!ladish_write_string(fd, "\" uuid=\""))
883 return false;
886 if (!ladish_write_string(fd, str))
888 return false;
891 if (!ladish_write_string(fd, "\">\n"))
893 return false;
896 if (!ladish_write_indented_string(fd, indent + 1, "<ports>\n"))
898 return false;
901 return true;
904 #define fd (((struct ladish_write_jack_context *)context)->fd)
905 #define indent (((struct ladish_write_jack_context *)context)->indent)
906 #define ctx_ptr ((struct ladish_write_jack_context *)context)
908 static
909 bool
910 ladish_save_jack_client_begin(
911 void * context,
912 ladish_graph_handle graph_handle,
913 bool hidden,
914 ladish_client_handle client_handle,
915 const char * client_name,
916 void ** client_iteration_context_ptr_ptr)
918 void * vgraph;
920 vgraph = ladish_client_get_vgraph(client_handle);
921 ctx_ptr->client_vgraph_match = vgraph == ctx_ptr->vgraph_filter;
922 ctx_ptr->a2j = ladish_virtualizer_is_a2j_client(client_handle);
924 /* for the a2j client vgraph is always the studio graph.
925 However if studio has no a2j ports, lets not write a2j client.
926 If there is a a2j port that matched the vgraph, the prolog will get written anyway */
927 ctx_ptr->client_visible =
928 (!hidden ||
929 ladish_client_has_app(client_handle)) &&
930 ctx_ptr->client_vgraph_match &&
931 !ctx_ptr->a2j;
932 if (!ctx_ptr->client_visible)
934 return true;
937 return ladish_save_jack_client_write_prolog(fd, indent, client_handle, client_name);
940 static
941 bool
942 ladish_save_jack_client_end(
943 void * context,
944 ladish_graph_handle graph_handle,
945 bool hidden,
946 ladish_client_handle client_handle,
947 const char * client_name,
948 void * client_iteration_context_ptr)
950 if (!ctx_ptr->client_visible)
952 return true;
955 if (!ladish_write_indented_string(fd, indent + 1, "</ports>\n"))
957 return false;
960 if (!ladish_write_indented_string(fd, indent, "</client>\n"))
962 return false;
965 return true;
968 static
969 bool
970 ladish_save_jack_port(
971 void * context,
972 ladish_graph_handle graph_handle,
973 bool hidden,
974 void * client_iteration_context_ptr,
975 ladish_client_handle client_handle,
976 const char * client_name,
977 ladish_port_handle port_handle,
978 const char * port_name,
979 uint32_t port_type,
980 uint32_t port_flags)
982 uuid_t uuid;
983 char str[37];
985 if (hidden && !ladish_port_has_app(port_handle))
987 return true;
990 /* check vgraph for a2j ports */
991 if (ctx_ptr->a2j && ctx_ptr->vgraph_filter == ladish_port_get_vgraph(port_handle))
993 if (!ctx_ptr->client_visible)
995 if (!ladish_save_jack_client_write_prolog(fd, indent, client_handle, client_name))
997 return false;
1000 ctx_ptr->client_visible = true;
1004 if (!ctx_ptr->client_visible)
1006 return true;
1009 /* skip hidden ports of running apps */
1010 if (hidden && !is_hidden_port_interesting(ctx_ptr->app_supervisor, client_handle, port_handle))
1012 return true;
1015 ladish_port_get_uuid(port_handle, uuid);
1016 uuid_unparse(uuid, str);
1018 log_info("saving jack port '%s':'%s' (%s)", client_name, port_name, str);
1020 if (!ladish_write_indented_string(fd, indent + 2, "<port name=\""))
1022 return false;
1025 if (!ladish_write_string_escape_ex(fd, port_name, LADISH_ESCAPE_FLAG_XML_ATTR))
1027 return false;
1030 if (!ladish_write_string(fd, "\" uuid=\""))
1032 return false;
1035 if (!ladish_write_string(fd, str))
1037 return false;
1040 if (!ladish_write_string(fd, "\" />\n"))
1042 return false;
1045 return true;
1048 #undef ctx_ptr
1049 #undef indent
1050 #undef fd
1052 bool ladish_write_jgraph(int fd, int indent, ladish_graph_handle vgraph, ladish_app_supervisor_handle app_supervisor)
1054 struct ladish_write_jack_context context;
1056 ladish_check_integrity();
1058 if (!ladish_write_indented_string(fd, indent, "<clients>\n"))
1060 return false;
1063 context.fd = fd;
1064 context.indent = indent + 1;
1065 context.vgraph_filter = vgraph;
1066 context.app_supervisor = app_supervisor;
1068 if (!ladish_graph_iterate_nodes(
1069 ladish_studio_get_jack_graph(),
1070 &context,
1071 ladish_save_jack_client_begin,
1072 ladish_save_jack_port,
1073 ladish_save_jack_client_end))
1075 log_error("ladish_graph_iterate_nodes() failed");
1076 return false;
1079 if (!ladish_write_indented_string(fd, indent, "</clients>\n"))
1081 return false;
1084 return true;