daemon: Router templates
[ladish.git] / daemon / room_load.c
blobcd72fcf3d814d74d2c0c94bf93dd3f2950f552f5
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 the parts of room object implementation
9 * that are related to project load functionality
10 **************************************************************************
12 * LADI Session Handler is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * LADI Session Handler is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with LADI Session Handler. If not, see <http://www.gnu.org/licenses/>
24 * or write to the Free Software Foundation, Inc.,
25 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <expat.h>
34 #include "room_internal.h"
35 #include "../catdup.h"
36 #include "load.h"
37 #include "../proxies/notify_proxy.h"
38 #include "escape.h"
39 #include "studio.h"
41 #define context_ptr ((struct ladish_parse_context *)data)
42 #define room_ptr ((struct ladish_room *)context_ptr->room)
44 static void callback_chrdata(void * data, const XML_Char * s, int len)
46 if (context_ptr->error)
48 return;
51 if (context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_PARAMETER ||
52 context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_KEY ||
53 context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_APPLICATION)
55 if (context_ptr->data_used + len >= sizeof(context_ptr->data))
57 log_error("xml parse max char data length reached");
58 context_ptr->error = XML_TRUE;
59 return;
62 memcpy(context_ptr->data + context_ptr->data_used, s, len);
63 context_ptr->data_used += len;
67 static void callback_elstart(void * data, const char * el, const char ** attr)
69 const char * name;
70 const char * uuid_str;
71 uuid_t uuid;
72 const char * uuid2_str;
73 uuid_t uuid2;
74 ladish_port_handle port1;
75 ladish_port_handle port2;
76 uint32_t port_type;
77 uint32_t port_flags;
79 if (context_ptr->error)
81 return;
84 if (context_ptr->depth + 1 >= MAX_STACK_DEPTH)
86 log_error("xml parse max stack depth reached");
87 context_ptr->error = XML_TRUE;
88 return;
91 if (strcmp(el, "project") == 0)
93 //log_info("<project>");
94 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_PROJECT;
96 if (!ladish_get_name_and_uuid_attributes("/project", attr, &name, &uuid_str, uuid))
98 context_ptr->error = XML_TRUE;
99 return;
102 log_info("Project '%s' with uuid %s", name, uuid_str);
104 room_ptr->project_name = strdup(name);
105 if (room_ptr->project_name == NULL)
107 log_error("strdup() failed for project name");
108 context_ptr->error = XML_TRUE;
109 return;
112 uuid_copy(room_ptr->project_uuid, uuid);
114 return;
117 if (strcmp(el, "jack") == 0)
119 //log_info("<jack>");
120 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_JACK;
121 return;
124 if (strcmp(el, "clients") == 0)
126 //log_info("<clients>");
127 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_CLIENTS;
128 return;
131 if (strcmp(el, "room") == 0)
133 //log_info("<room>");
134 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_ROOM;
135 return;
138 if (strcmp(el, "client") == 0)
140 //log_info("<client>");
141 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_CLIENT;
142 if (context_ptr->client != NULL)
144 log_error("nested clients");
145 context_ptr->error = XML_TRUE;
146 return;
149 if (context_ptr->depth == 3 &&
150 context_ptr->element[0] == PARSE_CONTEXT_PROJECT &&
151 context_ptr->element[1] == PARSE_CONTEXT_JACK &&
152 context_ptr->element[2] == PARSE_CONTEXT_CLIENTS)
154 if (!ladish_get_name_and_uuid_attributes("/project/jack/clients/client", attr, &name, &uuid_str, uuid))
156 context_ptr->error = XML_TRUE;
157 return;
160 log_info("jack client \"%s\" with uuid %s", name, uuid_str);
162 if (!ladish_client_create(uuid, &context_ptr->client))
164 log_error("ladish_client_create() failed.");
165 context_ptr->error = XML_TRUE;
166 ASSERT(context_ptr->client == NULL);
167 return;
170 if (!ladish_graph_add_client(ladish_studio_get_jack_graph(), context_ptr->client, name, true))
172 log_error("ladish_graph_add_client() failed to add client '%s' to JACK graph", name);
173 context_ptr->error = XML_TRUE;
174 ladish_client_destroy(context_ptr->client);
175 context_ptr->client = NULL;
178 return;
181 if (context_ptr->depth == 2 &&
182 context_ptr->element[0] == PARSE_CONTEXT_PROJECT &&
183 context_ptr->element[1] == PARSE_CONTEXT_CLIENTS)
185 if (!ladish_get_name_and_uuid_attributes("/studio/clients/client", attr, &name, &uuid_str, uuid))
187 context_ptr->error = XML_TRUE;
188 return;
191 log_info("studio client \"%s\" with uuid %s", name, uuid_str);
193 context_ptr->client = ladish_graph_find_client_by_uuid(room_ptr->graph, uuid);
194 if (context_ptr->client != NULL)
196 log_info("Found existing client");
197 return;
200 if (!ladish_client_create(uuid, &context_ptr->client))
202 log_error("ladish_client_create() failed.");
203 context_ptr->error = XML_TRUE;
204 ASSERT(context_ptr->client == NULL);
205 return;
208 if (!ladish_graph_add_client(room_ptr->graph, context_ptr->client, name, true))
210 log_error("ladish_graph_add_client() failed to add client '%s' to room graph", name);
211 context_ptr->error = XML_TRUE;
212 ladish_client_destroy(context_ptr->client);
213 context_ptr->client = NULL;
216 return;
219 log_error("client element in wrong place");
220 ladish_dump_element_stack(context_ptr);
221 context_ptr->error = XML_TRUE;
222 return;
225 if (strcmp(el, "ports") == 0)
227 //log_info("<ports>");
228 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_PORTS;
229 return;
232 if (strcmp(el, "port") == 0)
234 //log_info("<port>");
235 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_PORT;
237 if (context_ptr->port != NULL)
239 log_error("nested ports");
240 context_ptr->error = XML_TRUE;
241 return;
244 if (context_ptr->depth >= 3 &&
245 context_ptr->element[context_ptr->depth - 3] == PARSE_CONTEXT_CLIENTS &&
246 context_ptr->element[context_ptr->depth - 2] == PARSE_CONTEXT_CLIENT &&
247 context_ptr->element[context_ptr->depth - 1] == PARSE_CONTEXT_PORTS)
249 //log_info("client port");
250 if (context_ptr->client == NULL)
252 log_error("client-less port");
253 context_ptr->error = XML_TRUE;
254 return;
257 if (context_ptr->depth == 5 && context_ptr->element[0] == PARSE_CONTEXT_PROJECT && context_ptr->element[1] == PARSE_CONTEXT_JACK)
259 if (!ladish_get_name_and_uuid_attributes("/project/jack/clients/client/ports/port", attr, &name, &uuid_str, uuid))
261 context_ptr->error = XML_TRUE;
262 return;
265 log_info("jack port \"%s\" with uuid %s", name, uuid_str);
267 if (!ladish_port_create(uuid, false, &context_ptr->port))
269 log_error("ladish_port_create() failed.");
270 return;
273 if (!ladish_graph_add_port(ladish_studio_get_jack_graph(), context_ptr->client, context_ptr->port, name, 0, 0, true))
275 log_error("ladish_graph_add_port() failed.");
276 ladish_port_destroy(context_ptr->port);
277 context_ptr->port = NULL;
280 return;
282 else if (context_ptr->depth == 4 && context_ptr->element[0] == PARSE_CONTEXT_PROJECT)
284 if (!ladish_get_name_and_uuid_attributes("/project/clients/client/ports/port", attr, &name, &uuid_str, uuid))
286 context_ptr->error = XML_TRUE;
287 return;
290 context_ptr->port = ladish_graph_find_port_by_uuid(room_ptr->graph, uuid, false);
291 if (context_ptr->port != NULL)
293 if (!ladish_port_is_link(context_ptr->port))
295 log_info("port \"%s\" with uuid %s already exists in room graph and is not a room-studio link", name, uuid_str);
296 context_ptr->error = XML_TRUE;
298 return;
301 context_ptr->port = ladish_graph_find_port_by_uuid(ladish_studio_get_jack_graph(), uuid, false);
302 if (context_ptr->port == NULL)
304 log_info("app port \"%s\" with uuid %s not found in the jack graph", name, uuid_str);
305 context_ptr->error = XML_TRUE;
306 ladish_graph_dump(ladish_studio_get_jack_graph());
307 return;
310 log_info("app port \"%s\" with uuid %s", name, uuid_str);
312 if (!ladish_graph_add_port(room_ptr->graph, context_ptr->client, context_ptr->port, name, 0, 0, true))
314 log_error("ladish_graph_add_port() failed.");
315 ladish_port_destroy(context_ptr->port);
316 context_ptr->port = NULL;
317 return;
320 return;
323 else if (context_ptr->depth == 2 &&
324 context_ptr->element[0] == PARSE_CONTEXT_PROJECT &&
325 context_ptr->element[1] == PARSE_CONTEXT_ROOM)
327 ASSERT(context_ptr->room != NULL);
328 //log_info("room port");
330 if (!ladish_get_name_and_uuid_attributes("/project/room/port", attr, &name, &uuid_str, uuid))
332 context_ptr->error = XML_TRUE;
333 return;
336 log_info("room port \"%s\" with uuid %s", name, uuid_str);
338 if (!ladish_parse_port_type_and_direction_attributes("/project/room/port", attr, &port_type, &port_flags))
340 context_ptr->error = XML_TRUE;
341 return;
344 context_ptr->port = ladish_graph_find_port_by_uuid(room_ptr->graph, uuid, false);
345 if (context_ptr->port == NULL)
347 log_error("Cannot find room link port() failed.");
348 context_ptr->port = NULL;
351 return;
354 log_error("port element in wrong place");
355 ladish_dump_element_stack(context_ptr);
356 context_ptr->error = XML_TRUE;
358 return;
361 if (strcmp(el, "connections") == 0)
363 //log_info("<connections>");
364 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_CONNECTIONS;
365 return;
368 if (strcmp(el, "connection") == 0)
370 //log_info("<connection>");
371 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_CONNECTION;
373 uuid_str = ladish_get_uuid_attribute(attr, "port1", uuid, false);
374 if (uuid_str == NULL)
376 log_error("/studio/connections/connection \"port1\" attribute is not available.");
377 context_ptr->error = XML_TRUE;
378 return;
381 uuid2_str = ladish_get_uuid_attribute(attr, "port2", uuid2, false);
382 if (uuid2_str == NULL)
384 log_error("/studio/connections/connection \"port2\" attribute is not available.");
385 context_ptr->error = XML_TRUE;
386 return;
389 log_info("studio connection between port %s and port %s", uuid_str, uuid2_str);
391 port1 = ladish_graph_find_port_by_uuid(room_ptr->graph, uuid, true);
392 if (port1 == NULL)
394 log_error("studio client with unknown port %s", uuid_str);
395 context_ptr->error = XML_TRUE;
396 return;
399 port2 = ladish_graph_find_port_by_uuid(room_ptr->graph, uuid2, true);
400 if (port2 == NULL)
402 log_error("studio client with unknown port %s", uuid2_str);
403 context_ptr->error = XML_TRUE;
404 return;
407 context_ptr->connection_id = ladish_graph_add_connection(room_ptr->graph, port1, port2, true);
408 if (context_ptr->connection_id == 0)
410 log_error("ladish_graph_add_connection() failed.");
411 return;
414 return;
417 if (strcmp(el, "applications") == 0)
419 //log_info("<applications>");
420 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_APPLICATIONS;
421 return;
424 if (strcmp(el, "application") == 0)
426 //log_info("<application>");
427 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_APPLICATION;
429 name = ladish_get_string_attribute(attr, "name");
430 if (name == NULL)
432 log_error("application \"name\" attribute is not available.");
433 context_ptr->error = XML_TRUE;
434 return;
437 if (ladish_get_bool_attribute(attr, "terminal", &context_ptr->terminal) == NULL)
439 log_error("application \"terminal\" attribute is not available. name=\"%s\"", name);
440 context_ptr->error = XML_TRUE;
441 return;
444 if (ladish_get_bool_attribute(attr, "autorun", &context_ptr->autorun) == NULL)
446 log_error("application \"autorun\" attribute is not available. name=\"%s\"", name);
447 context_ptr->error = XML_TRUE;
448 return;
451 if (ladish_get_byte_attribute(attr, "level", &context_ptr->level) == NULL)
453 log_error("application \"level\" attribute is not available. name=\"%s\"", name);
454 context_ptr->error = XML_TRUE;
455 return;
458 context_ptr->str = strdup(name);
459 if (context_ptr->str == NULL)
461 log_error("strdup() failed");
462 context_ptr->error = XML_TRUE;
463 return;
466 context_ptr->data_used = 0;
467 return;
470 if (strcmp(el, "dict") == 0)
472 //log_info("<dict>");
473 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_DICT;
475 if (context_ptr->dict != NULL)
477 log_error("nested dicts");
478 context_ptr->error = XML_TRUE;
479 return;
482 if (context_ptr->depth == 1 &&
483 context_ptr->element[0] == PARSE_CONTEXT_STUDIO)
485 context_ptr->dict = ladish_graph_get_dict(room_ptr->graph);
486 ASSERT(context_ptr->dict != NULL);
488 else if (context_ptr->depth > 0 &&
489 context_ptr->element[context_ptr->depth - 1] == PARSE_CONTEXT_CLIENT)
491 ASSERT(context_ptr->client != NULL);
492 context_ptr->dict = ladish_client_get_dict(context_ptr->client);
493 ASSERT(context_ptr->dict != NULL);
495 else if (context_ptr->depth > 0 &&
496 context_ptr->element[context_ptr->depth - 1] == PARSE_CONTEXT_PORT)
498 ASSERT(context_ptr->port != NULL);
499 context_ptr->dict = ladish_port_get_dict(context_ptr->port);
500 ASSERT(context_ptr->dict != NULL);
502 else if (context_ptr->depth > 0 &&
503 context_ptr->element[context_ptr->depth - 1] == PARSE_CONTEXT_CONNECTION)
505 ASSERT(context_ptr->port != NULL);
506 context_ptr->dict = ladish_graph_get_connection_dict(room_ptr->graph, context_ptr->connection_id);
507 ASSERT(context_ptr->dict != NULL);
509 else
511 log_error("unexpected dict XML element");
512 context_ptr->error = XML_TRUE;
513 return;
516 return;
519 if (strcmp(el, "key") == 0)
521 //log_info("<key>");
522 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_KEY;
524 if (context_ptr->dict == NULL)
526 log_error("dict-less key");
527 context_ptr->error = XML_TRUE;
528 return;
531 name = ladish_get_string_attribute(attr, "name");
532 if (name == NULL)
534 log_error("dict/key \"name\" attribute is not available.");
535 context_ptr->error = XML_TRUE;
536 return;
539 context_ptr->str = strdup(name);
540 if (context_ptr->str == NULL)
542 log_error("strdup() failed");
543 context_ptr->error = XML_TRUE;
544 return;
547 context_ptr->data_used = 0;
549 return;
552 log_error("unknown element \"%s\"", el);
553 context_ptr->error = XML_TRUE;
556 static void callback_elend(void * data, const char * el)
558 if (context_ptr->error)
560 return;
563 //log_info("element end (depth = %d, element = %u)", context_ptr->depth, context_ptr->element[context_ptr->depth]);
565 if (context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_KEY &&
566 context_ptr->depth > 0 &&
567 context_ptr->element[context_ptr->depth - 1] == PARSE_CONTEXT_DICT)
569 ASSERT(context_ptr->dict != NULL);
570 context_ptr->data[context_ptr->data_used] = 0;
571 log_info("dict key '%s' with value '%s'", context_ptr->str, context_ptr->data);
572 if (!ladish_dict_set(context_ptr->dict, context_ptr->str, context_ptr->data))
574 log_error("ladish_dict_set() failed");
575 context_ptr->error = XML_TRUE;
576 return;
579 else if (context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_ROOM)
581 //log_info("</room>");
583 else if (context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_JACK)
585 //log_info("</jack>");
587 else if (context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_DICT)
589 //log_info("</dict>");
590 ASSERT(context_ptr->dict != NULL);
591 context_ptr->dict = NULL;
593 else if (context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_CLIENT)
595 //log_info("</client>");
596 ASSERT(context_ptr->client != NULL);
597 context_ptr->client = NULL;
599 else if (context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_PORT)
601 //log_info("</port>");
602 ASSERT(context_ptr->port != NULL);
603 context_ptr->port = NULL;
605 else if (context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_APPLICATION)
607 context_ptr->data[unescape(context_ptr->data, context_ptr->data_used, context_ptr->data)] = 0;
608 unescape(context_ptr->str, strlen(context_ptr->str) + 1, context_ptr->str);
610 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);
612 if (ladish_app_supervisor_add(room_ptr->app_supervisor, context_ptr->str, context_ptr->autorun, context_ptr->data, context_ptr->terminal, context_ptr->level) == NULL)
614 log_error("ladish_app_supervisor_add() failed.");
615 context_ptr->error = XML_TRUE;
619 context_ptr->depth--;
621 if (context_ptr->str != NULL)
623 free(context_ptr->str);
624 context_ptr->str = NULL;
627 return;
630 #undef room_ptr
631 #undef context_ptr
633 #define room_ptr ((struct ladish_room *)room_handle)
635 bool ladish_room_load_project(ladish_room_handle room_handle, const char * project_dir)
637 char * path;
638 struct stat st;
639 XML_Parser parser;
640 int bytes_read;
641 void * buffer;
642 int fd;
643 enum XML_Status xmls;
644 struct ladish_parse_context parse_context;
645 bool ret;
647 log_info("Loading project '%s' into room '%s'", project_dir, room_ptr->name);
649 ASSERT(room_ptr->project_dir == NULL);
650 ASSERT(room_ptr->project_name == NULL);
651 ASSERT(!ladish_app_supervisor_has_apps(room_ptr->app_supervisor));
653 ret = false;
655 room_ptr->project_dir = strdup(project_dir);
656 if (room_ptr->project_dir == NULL)
658 log_error("strdup() failed to for project dir");
659 goto exit;
662 path = catdup(project_dir, LADISH_PROJECT_FILENAME);
663 if (path == NULL)
665 log_error("catdup() failed to compose xml file path");
666 goto exit;
669 if (stat(path, &st) != 0)
671 log_error("failed to stat '%s': %d (%s)", path, errno, strerror(errno));
672 goto free_path;
675 fd = open(path, O_RDONLY);
676 if (fd == -1)
678 log_error("failed to open '%s': %d (%s)", path, errno, strerror(errno));
679 goto free_path;
682 parser = XML_ParserCreate(NULL);
683 if (parser == NULL)
685 log_error("XML_ParserCreate() failed to create parser object.");
686 goto close;
689 //log_info("conf file size is %llu bytes", (unsigned long long)st.st_size);
691 /* we are expecting that conf file has small enough size to fit in memory */
693 buffer = XML_GetBuffer(parser, st.st_size);
694 if (buffer == NULL)
696 log_error("XML_GetBuffer() failed.");
697 goto free_parser;
700 bytes_read = read(fd, buffer, st.st_size);
701 if (bytes_read != st.st_size)
703 log_error("read() returned unexpected result.");
704 goto free_parser;
707 //log_info("\n----------\n%s\n-----------\n", buffer);
708 //goto free_parser;
710 parse_context.error = XML_FALSE;
711 parse_context.depth = -1;
712 parse_context.str = NULL;
713 parse_context.client = NULL;
714 parse_context.port = NULL;
715 parse_context.dict = NULL;
716 parse_context.room = room_handle;
718 XML_SetElementHandler(parser, callback_elstart, callback_elend);
719 XML_SetCharacterDataHandler(parser, callback_chrdata);
720 XML_SetUserData(parser, &parse_context);
722 xmls = XML_ParseBuffer(parser, bytes_read, XML_TRUE);
723 if (xmls == XML_STATUS_ERROR && !parse_context.error)
725 log_error("XML_ParseBuffer() failed.");
727 if (xmls == XML_STATUS_ERROR || parse_context.error)
729 goto free_parser;
732 ladish_interlink_clients(room_ptr->graph, room_ptr->app_supervisor);
733 /* ladish_graph_dump(ladish_studio_get_jack_graph()); */
734 /* ladish_graph_dump(room_ptr->graph); */
736 ladish_app_supervisor_set_directory(room_ptr->app_supervisor, project_dir);
737 ladish_graph_trick_dicts(room_ptr->graph);
738 ladish_app_supervisor_autorun(room_ptr->app_supervisor);
740 ladish_room_emit_project_properties_changed(room_ptr);
742 ret = true;
744 free_parser:
745 XML_ParserFree(parser);
746 close:
747 close(fd);
748 free_path:
749 free(path);
750 exit:
751 if (!ret)
753 ladish_room_unload_project(room_handle);
754 ladish_notify_simple(LADISH_NOTIFY_URGENCY_HIGH, "Project load failed", "Please inspect the ladishd log (~/.ladish/ladish.log) for more info");
757 return ret;
760 #undef room_ptr