daemon: smarter parsing of studio xml files
[ladish.git] / daemon / cmd_load_studio.c
blob7f094d5db7d4c1713eab7553583201a34a3e5f05
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 "load 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>
33 #include <expat.h>
34 #include <limits.h>
36 #include "escape.h"
37 #include "cmd.h"
38 #include "studio_internal.h"
40 #define PARSE_CONTEXT_ROOT 0
41 #define PARSE_CONTEXT_STUDIO 1
42 #define PARSE_CONTEXT_JACK 2
43 #define PARSE_CONTEXT_CONF 3
44 #define PARSE_CONTEXT_PARAMETER 4
45 #define PARSE_CONTEXT_CLIENTS 5
46 #define PARSE_CONTEXT_CLIENT 6
47 #define PARSE_CONTEXT_PORTS 7
48 #define PARSE_CONTEXT_PORT 8
49 #define PARSE_CONTEXT_DICT 9
50 #define PARSE_CONTEXT_KEY 10
51 #define PARSE_CONTEXT_CONNECTIONS 11
52 #define PARSE_CONTEXT_CONNECTION 12
53 #define PARSE_CONTEXT_APPLICATIONS 13
54 #define PARSE_CONTEXT_APPLICATION 14
56 #define MAX_STACK_DEPTH 10
57 #define MAX_DATA_SIZE 10240
59 struct parse_context
61 XML_Bool error;
62 unsigned int element[MAX_STACK_DEPTH];
63 signed int depth;
64 char data[MAX_DATA_SIZE];
65 int data_used;
66 char * str;
67 ladish_client_handle client;
68 ladish_port_handle port;
69 ladish_dict_handle dict;
70 uint64_t connection_id;
71 bool terminal;
72 bool autorun;
73 uint8_t level;
76 static const char * get_string_attribute(const char * const * attr, const char * key)
78 while (attr[0] != NULL)
80 ASSERT(attr[1] != NULL);
81 if (strcmp(attr[0], key) == 0)
83 return attr[1];
85 attr += 2;
88 log_error("attribute \"%s\" is missing");
89 return NULL;
92 static const char * get_uuid_attribute(const char * const * attr, const char * key, uuid_t uuid)
94 const char * value;
96 value = get_string_attribute(attr, key);
97 if (value == NULL)
99 return NULL;
102 if (uuid_parse(value, uuid) != 0)
104 log_error("cannot parse uuid \"%s\"", value);
105 return NULL;
108 return value;
111 static const char * get_bool_attribute(const char * const * attr, const char * key, bool * bool_value_ptr)
113 const char * value_str;
115 value_str = get_string_attribute(attr, key);
116 if (value_str == NULL)
118 return NULL;
121 if (strcmp(value_str, "true") == 0)
123 *bool_value_ptr = true;
124 return value_str;
127 if (strcmp(value_str, "false") == 0)
129 *bool_value_ptr = false;
130 return value_str;
133 log_error("boolean XML attribute has value of \"%s\" but only \"true\" and \"false\" are valid", value_str);
134 return NULL;
137 static const char * get_byte_attribute(const char * const * attr, const char * key, uint8_t * byte_value_ptr)
139 const char * value_str;
140 long int li_value;
141 char * end_ptr;
143 value_str = get_string_attribute(attr, key);
144 if (value_str == NULL)
146 return NULL;
149 errno = 0; /* To distinguish success/failure after call */
150 li_value = strtol(value_str, &end_ptr, 10);
151 if ((errno == ERANGE && (li_value == LONG_MAX || li_value == LONG_MIN)) || (errno != 0 && li_value == 0) || end_ptr == value_str)
153 log_error("value '%s' of attribute '%s' is not valid integer.", value_str, key);
154 return NULL;
157 if (li_value < 0 || li_value > 255)
159 log_error("value '%s' of attribute '%s' is not valid uint8.", value_str, key);
160 return NULL;
163 *byte_value_ptr = (uint8_t)li_value;
164 return value_str;
167 static
168 bool
169 get_name_and_uuid_attributes(
170 const char * element_description,
171 const char * const * attr,
172 const char ** name_str_ptr,
173 const char ** uuid_str_ptr,
174 uuid_t uuid)
176 const char * name_str;
177 const char * uuid_str;
179 name_str = get_string_attribute(attr, "name");
180 if (name_str == NULL)
182 log_error("%s \"name\" attribute is not available", element_description);
183 return false;
186 uuid_str = get_uuid_attribute(attr, "uuid", uuid);
187 if (uuid_str == NULL)
189 log_error("%s \"uuid\" attribute is not available. name=\"%s\"", element_description, name_str);
190 return false;
193 *name_str_ptr = name_str;
194 *uuid_str_ptr = uuid_str;
195 return true;
198 #define context_ptr ((struct parse_context *)data)
200 static void callback_chrdata(void * data, const XML_Char * s, int len)
202 if (context_ptr->error)
204 return;
207 if (context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_PARAMETER ||
208 context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_KEY ||
209 context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_APPLICATION)
211 if (context_ptr->data_used + len >= sizeof(context_ptr->data))
213 log_error("xml parse max char data length reached");
214 context_ptr->error = XML_TRUE;
215 return;
218 memcpy(context_ptr->data + context_ptr->data_used, s, len);
219 context_ptr->data_used += len;
223 static void callback_elstart(void * data, const char * el, const char ** attr)
225 const char * name;
226 const char * path;
227 const char * uuid_str;
228 uuid_t uuid;
229 const char * uuid2_str;
230 uuid_t uuid2;
231 ladish_port_handle port1;
232 ladish_port_handle port2;
234 if (context_ptr->error)
236 return;
239 if (context_ptr->depth + 1 >= MAX_STACK_DEPTH)
241 log_error("xml parse max stack depth reached");
242 context_ptr->error = XML_TRUE;
243 return;
246 if (strcmp(el, "studio") == 0)
248 //log_info("<studio>");
249 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_STUDIO;
250 return;
253 if (strcmp(el, "jack") == 0)
255 //log_info("<jack>");
256 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_JACK;
257 return;
260 if (strcmp(el, "conf") == 0)
262 //log_info("<conf>");
263 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_CONF;
264 return;
267 if (strcmp(el, "parameter") == 0)
269 //log_info("<parameter>");
270 path = get_string_attribute(attr, "path");
271 if (path == NULL)
273 log_error("<parameter> XML element without \"path\" attribute");
274 context_ptr->error = XML_TRUE;
275 return;
278 context_ptr->str = strdup(path);
279 if (context_ptr->str == NULL)
281 log_error("strdup() failed");
282 context_ptr->error = XML_TRUE;
283 return;
286 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_PARAMETER;
287 context_ptr->data_used = 0;
288 return;
291 if (strcmp(el, "clients") == 0)
293 //log_info("<clients>");
294 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_CLIENTS;
295 return;
298 if (strcmp(el, "client") == 0)
300 //log_info("<client>");
301 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_CLIENT;
303 if (context_ptr->client != NULL)
305 log_error("nested clients");
306 context_ptr->error = XML_TRUE;
307 return;
310 if (context_ptr->depth == 3 &&
311 context_ptr->element[0] == PARSE_CONTEXT_STUDIO &&
312 context_ptr->element[1] == PARSE_CONTEXT_JACK &&
313 context_ptr->element[2] == PARSE_CONTEXT_CLIENTS)
315 if (!get_name_and_uuid_attributes("/studio/jack/clients/client", attr, &name, &uuid_str, uuid))
317 context_ptr->error = XML_TRUE;
318 return;
321 log_info("jack client \"%s\" with uuid %s", name, uuid_str);
323 if (!ladish_client_create(uuid, &context_ptr->client))
325 log_error("ladish_client_create() failed.");
326 context_ptr->error = XML_TRUE;
327 ASSERT(context_ptr->client == NULL);
328 return;
331 if (!ladish_graph_add_client(g_studio.jack_graph, context_ptr->client, name, true))
333 log_error("ladish_graph_add_client() failed to add client '%s' to JACK graph", name);
334 context_ptr->error = XML_TRUE;
335 ladish_client_destroy(context_ptr->client);
336 context_ptr->client = NULL;
337 return;
340 else if (context_ptr->depth == 2 &&
341 context_ptr->element[0] == PARSE_CONTEXT_STUDIO &&
342 context_ptr->element[1] == PARSE_CONTEXT_CLIENTS)
344 if (!get_name_and_uuid_attributes("/studio/clients/client", attr, &name, &uuid_str, uuid))
346 context_ptr->error = XML_TRUE;
347 return;
350 log_info("studio client \"%s\" with uuid %s", name, uuid_str);
352 if (!ladish_client_create(uuid, &context_ptr->client))
354 log_error("ladish_client_create() failed.");
355 context_ptr->error = XML_TRUE;
356 ASSERT(context_ptr->client == NULL);
357 return;
360 if (!ladish_graph_add_client(g_studio.studio_graph, context_ptr->client, name, true))
362 log_error("ladish_graph_add_client() failed to add client '%s' to studio graph", name);
363 context_ptr->error = XML_TRUE;
364 ladish_client_destroy(context_ptr->client);
365 context_ptr->client = NULL;
366 return;
370 return;
373 if (strcmp(el, "ports") == 0)
375 //log_info("<ports>");
376 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_PORTS;
377 return;
380 if (strcmp(el, "port") == 0)
382 //log_info("<port>");
383 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_PORT;
385 if (context_ptr->port != NULL)
387 log_error("nested ports");
388 context_ptr->error = XML_TRUE;
389 return;
392 if (context_ptr->client == NULL)
394 log_error("client-less port");
395 context_ptr->error = XML_TRUE;
396 return;
399 if (context_ptr->depth == 5 &&
400 context_ptr->element[0] == PARSE_CONTEXT_STUDIO &&
401 context_ptr->element[1] == PARSE_CONTEXT_JACK &&
402 context_ptr->element[2] == PARSE_CONTEXT_CLIENTS &&
403 context_ptr->element[3] == PARSE_CONTEXT_CLIENT &&
404 context_ptr->element[4] == PARSE_CONTEXT_PORTS)
406 if (!get_name_and_uuid_attributes("/studio/jack/clients/client/ports/port", attr, &name, &uuid_str, uuid))
408 context_ptr->error = XML_TRUE;
409 return;
412 log_info("jack port \"%s\" with uuid %s", name, uuid_str);
414 if (!ladish_port_create(uuid, false, &context_ptr->port))
416 log_error("ladish_port_create() failed.");
417 return;
420 if (!ladish_graph_add_port(g_studio.jack_graph, context_ptr->client, context_ptr->port, name, 0, 0, true))
422 log_error("ladish_graph_add_port() failed.");
423 ladish_port_destroy(context_ptr->port);
424 context_ptr->port = NULL;
425 return;
428 else if (context_ptr->depth == 4 &&
429 context_ptr->element[0] == PARSE_CONTEXT_STUDIO &&
430 context_ptr->element[1] == PARSE_CONTEXT_CLIENTS &&
431 context_ptr->element[2] == PARSE_CONTEXT_CLIENT &&
432 context_ptr->element[3] == PARSE_CONTEXT_PORTS)
434 if (!get_name_and_uuid_attributes("/studio/clients/client/ports/port", attr, &name, &uuid_str, uuid))
436 context_ptr->error = XML_TRUE;
437 return;
440 log_info("studio port \"%s\" with uuid %s", name, uuid_str);
442 context_ptr->port = ladish_graph_find_port_by_uuid(g_studio.jack_graph, uuid, false);
443 if (context_ptr->port == NULL)
445 log_error("studio client with non-jack port %s", uuid_str);
446 context_ptr->error = XML_TRUE;
447 return;
450 if (!ladish_graph_add_port(g_studio.studio_graph, context_ptr->client, context_ptr->port, name, 0, 0, true))
452 log_error("ladish_graph_add_port() failed.");
453 ladish_port_destroy(context_ptr->port);
454 context_ptr->port = NULL;
455 return;
459 return;
462 if (strcmp(el, "connections") == 0)
464 //log_info("<connections>");
465 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_CONNECTIONS;
466 return;
469 if (strcmp(el, "connection") == 0)
471 //log_info("<connection>");
472 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_CONNECTION;
474 uuid_str = get_uuid_attribute(attr, "port1", uuid);
475 if (uuid_str == NULL)
477 log_error("/studio/connections/connection \"port1\" attribute is not available.");
478 context_ptr->error = XML_TRUE;
479 return;
482 uuid2_str = get_uuid_attribute(attr, "port2", uuid2);
483 if (uuid2_str == NULL)
485 log_error("/studio/connections/connection \"port2\" attribute is not available.");
486 context_ptr->error = XML_TRUE;
487 return;
490 log_info("studio connection between port %s and port %s", uuid_str, uuid2_str);
492 port1 = ladish_graph_find_port_by_uuid(g_studio.studio_graph, uuid, false);
493 if (port1 == NULL)
495 log_error("studio client with unknown port %s", uuid_str);
496 context_ptr->error = XML_TRUE;
497 return;
500 port2 = ladish_graph_find_port_by_uuid(g_studio.studio_graph, uuid2, false);
501 if (port2 == NULL)
503 log_error("studio client with unknown port %s", uuid2_str);
504 context_ptr->error = XML_TRUE;
505 return;
508 context_ptr->connection_id = ladish_graph_add_connection(g_studio.studio_graph, port1, port2, true);
509 if (context_ptr->connection_id == 0)
511 log_error("ladish_graph_add_connection() failed.");
512 return;
515 return;
518 if (strcmp(el, "applications") == 0)
520 //log_info("<applications>");
521 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_APPLICATIONS;
522 return;
525 if (strcmp(el, "application") == 0)
527 //log_info("<application>");
528 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_APPLICATION;
530 name = get_string_attribute(attr, "name");
531 if (name == NULL)
533 log_error("application \"name\" attribute is not available.");
534 context_ptr->error = XML_TRUE;
535 return;
538 if (get_bool_attribute(attr, "terminal", &context_ptr->terminal) == NULL)
540 log_error("application \"terminal\" attribute is not available. name=\"%s\"", name);
541 context_ptr->error = XML_TRUE;
542 return;
545 if (get_bool_attribute(attr, "autorun", &context_ptr->autorun) == NULL)
547 log_error("application \"autorun\" attribute is not available. name=\"%s\"", name);
548 context_ptr->error = XML_TRUE;
549 return;
552 if (get_byte_attribute(attr, "level", &context_ptr->level) == NULL)
554 log_error("application \"level\" attribute is not available. name=\"%s\"", name);
555 context_ptr->error = XML_TRUE;
556 return;
559 context_ptr->str = strdup(name);
560 if (context_ptr->str == NULL)
562 log_error("strdup() failed");
563 context_ptr->error = XML_TRUE;
564 return;
567 context_ptr->data_used = 0;
568 return;
571 if (strcmp(el, "dict") == 0)
573 //log_info("<dict>");
574 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_DICT;
576 if (context_ptr->dict != NULL)
578 log_error("nested dicts");
579 context_ptr->error = XML_TRUE;
580 return;
583 if (context_ptr->depth == 1 &&
584 context_ptr->element[0] == PARSE_CONTEXT_STUDIO)
586 context_ptr->dict = ladish_graph_get_dict(g_studio.studio_graph);
587 ASSERT(context_ptr->dict != NULL);
589 else if (context_ptr->depth > 0 &&
590 context_ptr->element[context_ptr->depth - 1] == PARSE_CONTEXT_CLIENT)
592 ASSERT(context_ptr->client != NULL);
593 context_ptr->dict = ladish_client_get_dict(context_ptr->client);
594 ASSERT(context_ptr->dict != NULL);
596 else if (context_ptr->depth > 0 &&
597 context_ptr->element[context_ptr->depth - 1] == PARSE_CONTEXT_PORT)
599 ASSERT(context_ptr->port != NULL);
600 context_ptr->dict = ladish_port_get_dict(context_ptr->port);
601 ASSERT(context_ptr->dict != NULL);
603 else if (context_ptr->depth > 0 &&
604 context_ptr->element[context_ptr->depth - 1] == PARSE_CONTEXT_CONNECTION)
606 ASSERT(context_ptr->port != NULL);
607 context_ptr->dict = ladish_graph_get_connection_dict(g_studio.studio_graph, context_ptr->connection_id);
608 ASSERT(context_ptr->dict != NULL);
610 else
612 log_error("unexpected dict XML element");
613 context_ptr->error = XML_TRUE;
614 return;
617 return;
620 if (strcmp(el, "key") == 0)
622 //log_info("<key>");
623 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_KEY;
625 if (context_ptr->dict == NULL)
627 log_error("dict-less key");
628 context_ptr->error = XML_TRUE;
629 return;
632 name = get_string_attribute(attr, "name");
633 if (name == NULL)
635 log_error("dict/key \"name\" attribute is not available.");
636 context_ptr->error = XML_TRUE;
637 return;
640 context_ptr->str = strdup(name);
641 if (context_ptr->str == NULL)
643 log_error("strdup() failed");
644 context_ptr->error = XML_TRUE;
645 return;
648 context_ptr->data_used = 0;
650 return;
653 log_error("unknown element \"%s\"", el);
654 context_ptr->error = XML_TRUE;
657 static void callback_elend(void * data, const char * el)
659 char * src;
660 char * dst;
661 char * sep;
662 size_t src_len;
663 size_t dst_len;
664 char * address;
665 struct jack_parameter_variant parameter;
666 bool is_set;
668 if (context_ptr->error)
670 return;
673 //log_info("element end (depth = %d, element = %u)", context_ptr->depth, context_ptr->element[context_ptr->depth]);
675 if (context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_PARAMETER &&
676 context_ptr->depth == 3 &&
677 context_ptr->element[0] == PARSE_CONTEXT_STUDIO &&
678 context_ptr->element[1] == PARSE_CONTEXT_JACK &&
679 context_ptr->element[2] == PARSE_CONTEXT_CONF)
681 context_ptr->data[context_ptr->data_used] = 0;
683 //log_info("'%s' with value '%s'", context_ptr->str, context_ptr->data);
685 dst = address = strdup(context_ptr->str);
686 src = context_ptr->str + 1;
687 while (*src != 0)
689 sep = strchr(src, '/');
690 if (sep == NULL)
692 src_len = strlen(src);
694 else
696 src_len = sep - src;
699 dst_len = unescape(src, src_len, dst);
700 dst[dst_len] = 0;
701 dst += dst_len + 1;
703 src += src_len;
704 ASSERT(*src == '/' || *src == 0);
705 if (sep != NULL)
707 ASSERT(*src == '/');
708 src++; /* skip separator */
710 else
712 ASSERT(*src == 0);
715 *dst = 0; /* ASCIZZ */
717 if (!jack_proxy_get_parameter_value(address, &is_set, &parameter))
719 log_error("jack_proxy_get_parameter_value() failed");
720 goto fail_free_address;
723 if (parameter.type == jack_string)
725 free(parameter.value.string);
728 switch (parameter.type)
730 case jack_boolean:
731 log_info("%s value is %s (boolean)", context_ptr->str, context_ptr->data);
732 if (strcmp(context_ptr->data, "true") == 0)
734 parameter.value.boolean = true;
736 else if (strcmp(context_ptr->data, "false") == 0)
738 parameter.value.boolean = false;
740 else
742 log_error("bad value for a bool jack param");
743 goto fail_free_address;
745 break;
746 case jack_string:
747 log_info("%s value is %s (string)", context_ptr->str, context_ptr->data);
748 parameter.value.string = context_ptr->data;
749 break;
750 case jack_byte:
751 log_debug("%s value is %u/%c (byte/char)", context_ptr->str, *context_ptr->data, *context_ptr->data);
752 if (context_ptr->data[0] == 0 ||
753 context_ptr->data[1] != 0)
755 log_error("bad value for a char jack param");
756 goto fail_free_address;
758 parameter.value.byte = context_ptr->data[0];
759 break;
760 case jack_uint32:
761 log_info("%s value is %s (uint32)", context_ptr->str, context_ptr->data);
762 if (sscanf(context_ptr->data, "%" PRIu32, &parameter.value.uint32) != 1)
764 log_error("bad value for an uint32 jack param");
765 goto fail_free_address;
767 break;
768 case jack_int32:
769 log_info("%s value is %s (int32)", context_ptr->str, context_ptr->data);
770 if (sscanf(context_ptr->data, "%" PRIi32, &parameter.value.int32) != 1)
772 log_error("bad value for an int32 jack param");
773 goto fail_free_address;
775 break;
776 default:
777 log_error("unknown jack parameter type %d of %s", (int)parameter.type, context_ptr->str);
778 goto fail_free_address;
781 if (!jack_proxy_set_parameter_value(address, &parameter))
783 log_error("jack_proxy_set_parameter_value() failed");
784 goto fail_free_address;
787 free(address);
789 else if (context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_KEY &&
790 context_ptr->depth > 0 &&
791 context_ptr->element[context_ptr->depth - 1] == PARSE_CONTEXT_DICT)
793 ASSERT(context_ptr->dict != NULL);
794 context_ptr->data[context_ptr->data_used] = 0;
795 log_info("dict key '%s' with value '%s'", context_ptr->str, context_ptr->data);
796 if (!ladish_dict_set(context_ptr->dict, context_ptr->str, context_ptr->data))
798 log_error("ladish_dict_set() failed");
799 context_ptr->error = XML_TRUE;
800 return;
803 else if (context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_DICT)
805 //log_info("</dict>");
806 ASSERT(context_ptr->dict != NULL);
807 context_ptr->dict = NULL;
809 else if (context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_CLIENT)
811 //log_info("</client>");
812 ASSERT(context_ptr->client != NULL);
813 context_ptr->client = NULL;
815 else if (context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_PORT)
817 //log_info("</port>");
818 ASSERT(context_ptr->port != NULL);
819 context_ptr->port = NULL;
821 else if (context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_APPLICATION)
823 context_ptr->data[unescape(context_ptr->data, context_ptr->data_used, context_ptr->data)] = 0;
824 unescape(context_ptr->str, strlen(context_ptr->str) + 1, context_ptr->str);
826 log_info("application '%s' (%s, %s, level %u) with commandline '%s'", context_ptr->str, context_ptr->terminal ? "terminal" : "shell", context_ptr->autorun ? "autorun" : "stopped", (unsigned int)context_ptr->level, context_ptr->data);
828 if (ladish_app_supervisor_add(g_studio.app_supervisor, context_ptr->str, context_ptr->autorun, context_ptr->data, context_ptr->terminal, context_ptr->level) == NULL)
830 log_error("ladish_app_supervisor_add() failed.");
831 context_ptr->error = XML_TRUE;
835 context_ptr->depth--;
837 if (context_ptr->str != NULL)
839 free(context_ptr->str);
840 context_ptr->str = NULL;
843 return;
845 fail_free_address:
846 free(address);
847 context_ptr->error = XML_TRUE;
848 return;
851 #undef context_ptr
853 struct ladish_command_load_studio
855 struct ladish_command command;
856 char * studio_name;
859 static
860 bool
861 interlink_client(
862 void * context,
863 ladish_client_handle jclient,
864 const char * name,
865 void ** client_iteration_context_ptr_ptr)
867 uuid_t uuid;
868 ladish_client_handle vclient;
870 if (strcmp(name, "system") == 0)
872 return true;
875 if (ladish_client_get_interlink(jclient, uuid))
877 ASSERT_NO_PASS; /* interlinks are not stored in xml yet */
878 return true;
881 vclient = ladish_graph_find_client_by_name(g_studio.studio_graph, name);
882 if (vclient == NULL)
884 log_error("JACK client '%s' has no vclient associated", name);
885 return true;
888 log_info("Interlinking clients of app '%s'", name);
889 ladish_client_interlink(jclient, vclient);
890 return true;
893 static void interlink_clients(void)
895 ladish_graph_iterate_nodes(g_studio.jack_graph, false, NULL, NULL, interlink_client, NULL, NULL);
898 #define cmd_ptr ((struct ladish_command_load_studio *)command_context)
900 static bool run(void * command_context)
902 char * path;
903 struct stat st;
904 XML_Parser parser;
905 int bytes_read;
906 void * buffer;
907 int fd;
908 enum XML_Status xmls;
909 struct parse_context parse_context;
911 ASSERT(cmd_ptr->command.state == LADISH_COMMAND_STATE_PENDING);
913 if (!ladish_studio_compose_filename(cmd_ptr->studio_name, &path, NULL))
915 log_error("failed to compose path of studio \%s\" file", cmd_ptr->studio_name);
916 return false;
919 log_info("Loading studio... ('%s')", path);
921 if (stat(path, &st) != 0)
923 log_error("failed to stat '%s': %d (%s)", path, errno, strerror(errno));
924 free(path);
925 return false;
928 g_studio.name = cmd_ptr->studio_name;
929 cmd_ptr->studio_name = NULL;
931 g_studio.filename = path;
933 if (!jack_reset_all_params())
935 log_error("jack_reset_all_params() failed");
936 return false;
939 fd = open(path, O_RDONLY);
940 if (fd == -1)
942 log_error("failed to open '%s': %d (%s)", path, errno, strerror(errno));
943 return false;
946 parser = XML_ParserCreate(NULL);
947 if (parser == NULL)
949 log_error("XML_ParserCreate() failed to create parser object.");
950 close(fd);
951 return false;
954 //log_info("conf file size is %llu bytes", (unsigned long long)st.st_size);
956 /* we are expecting that conf file has small enough size to fit in memory */
958 buffer = XML_GetBuffer(parser, st.st_size);
959 if (buffer == NULL)
961 log_error("XML_GetBuffer() failed.");
962 XML_ParserFree(parser);
963 close(fd);
964 return false;
967 bytes_read = read(fd, buffer, st.st_size);
968 if (bytes_read != st.st_size)
970 log_error("read() returned unexpected result.");
971 XML_ParserFree(parser);
972 close(fd);
973 return false;
976 parse_context.error = XML_FALSE;
977 parse_context.depth = -1;
978 parse_context.str = NULL;
979 parse_context.client = NULL;
980 parse_context.port = NULL;
981 parse_context.dict = NULL;
983 XML_SetElementHandler(parser, callback_elstart, callback_elend);
984 XML_SetCharacterDataHandler(parser, callback_chrdata);
985 XML_SetUserData(parser, &parse_context);
987 xmls = XML_ParseBuffer(parser, bytes_read, XML_TRUE);
988 if (xmls == XML_STATUS_ERROR)
990 if (!parse_context.error)
992 log_error("XML_ParseBuffer() failed.");
994 XML_ParserFree(parser);
995 close(fd);
996 return false;
999 XML_ParserFree(parser);
1000 close(fd);
1002 if (parse_context.error)
1004 return false;
1007 interlink_clients();
1009 g_studio.persisted = true;
1010 log_info("Studio loaded. ('%s')", path);
1012 ladish_graph_dump(g_studio.jack_graph);
1013 ladish_graph_dump(g_studio.studio_graph);
1015 if (!ladish_studio_publish())
1017 log_error("studio_publish() failed.");
1018 return false;
1021 cmd_ptr->command.state = LADISH_COMMAND_STATE_DONE;
1022 return true;
1025 static void destructor(void * command_context)
1027 log_info("load studio command destructor");
1028 if (cmd_ptr->studio_name != NULL)
1030 free(cmd_ptr->studio_name);
1034 #undef cmd_ptr
1036 bool ladish_command_load_studio(void * call_ptr, struct ladish_cqueue * queue_ptr, const char * studio_name)
1038 struct ladish_command_load_studio * cmd_ptr;
1039 char * studio_name_dup;
1041 studio_name_dup = strdup(studio_name);
1042 if (studio_name_dup == NULL)
1044 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "strdup('%s') failed.", studio_name);
1045 goto fail;
1048 if (!ladish_command_unload_studio(call_ptr, queue_ptr))
1050 goto fail_free_name;
1053 cmd_ptr = ladish_command_new(sizeof(struct ladish_command_load_studio));
1054 if (cmd_ptr == NULL)
1056 log_error("ladish_command_new() failed.");
1057 goto fail_drop_unload_command;
1060 cmd_ptr->command.run = run;
1061 cmd_ptr->command.destructor = destructor;
1062 cmd_ptr->studio_name = studio_name_dup;
1064 if (!ladish_cqueue_add_command(queue_ptr, &cmd_ptr->command))
1066 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "ladish_cqueue_add_command() failed.");
1067 goto fail_destroy_command;
1070 if (!ladish_command_start_studio(call_ptr, queue_ptr))
1072 goto fail_drop_load_command;
1075 return true;
1077 fail_drop_load_command:
1078 ladish_cqueue_drop_command(queue_ptr);
1080 fail_destroy_command:
1081 free(cmd_ptr);
1083 fail_drop_unload_command:
1084 ladish_cqueue_drop_command(queue_ptr);
1086 fail_free_name:
1087 free(studio_name_dup);
1089 fail:
1090 return false;