ladishd: Basic recent projects functionality
[ladish.git] / daemon / room_load.c
blob70a1acabe392885c2eb77b3f58d02add3da5a2cb
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 "../common/catdup.h"
36 #include "load.h"
37 #include "../proxies/notify_proxy.h"
38 #include "escape.h"
39 #include "studio.h"
40 #include "recent_projects.h"
42 #define context_ptr ((struct ladish_parse_context *)data)
43 #define room_ptr ((struct ladish_room *)context_ptr->room)
45 static void callback_chrdata(void * data, const XML_Char * s, int len)
47 if (context_ptr->error)
49 return;
52 if (context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_PARAMETER ||
53 context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_KEY ||
54 context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_APPLICATION)
56 if (context_ptr->data_used + len >= sizeof(context_ptr->data))
58 log_error("xml parse max char data length reached");
59 context_ptr->error = XML_TRUE;
60 return;
63 memcpy(context_ptr->data + context_ptr->data_used, s, len);
64 context_ptr->data_used += len;
68 static void callback_elstart(void * data, const char * el, const char ** attr)
70 const char * name;
71 const char * uuid_str;
72 uuid_t uuid;
73 const char * uuid2_str;
74 uuid_t uuid2;
75 ladish_port_handle port1;
76 ladish_port_handle port2;
77 uint32_t port_type;
78 uint32_t port_flags;
80 if (context_ptr->error)
82 return;
85 if (context_ptr->depth + 1 >= MAX_STACK_DEPTH)
87 log_error("xml parse max stack depth reached");
88 context_ptr->error = XML_TRUE;
89 return;
92 if (strcmp(el, "project") == 0)
94 //log_info("<project>");
95 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_PROJECT;
97 if (!ladish_get_name_and_uuid_attributes("/project", attr, &name, &uuid_str, uuid))
99 context_ptr->error = XML_TRUE;
100 return;
103 log_info("Project '%s' with uuid %s", name, uuid_str);
105 room_ptr->project_name = strdup(name);
106 if (room_ptr->project_name == NULL)
108 log_error("strdup() failed for project name");
109 context_ptr->error = XML_TRUE;
110 return;
113 uuid_copy(room_ptr->project_uuid, uuid);
115 return;
118 if (strcmp(el, "jack") == 0)
120 //log_info("<jack>");
121 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_JACK;
122 return;
125 if (strcmp(el, "clients") == 0)
127 //log_info("<clients>");
128 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_CLIENTS;
129 return;
132 if (strcmp(el, "room") == 0)
134 //log_info("<room>");
135 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_ROOM;
136 return;
139 if (strcmp(el, "client") == 0)
141 //log_info("<client>");
142 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_CLIENT;
143 if (context_ptr->client != NULL)
145 log_error("nested clients");
146 context_ptr->error = XML_TRUE;
147 return;
150 if (context_ptr->depth == 3 &&
151 context_ptr->element[0] == PARSE_CONTEXT_PROJECT &&
152 context_ptr->element[1] == PARSE_CONTEXT_JACK &&
153 context_ptr->element[2] == PARSE_CONTEXT_CLIENTS)
155 if (!ladish_get_name_and_uuid_attributes("/project/jack/clients/client", attr, &name, &uuid_str, uuid))
157 context_ptr->error = XML_TRUE;
158 return;
161 log_info("jack client \"%s\" with uuid %s", name, uuid_str);
163 if (!ladish_client_create(uuid, &context_ptr->client))
165 log_error("ladish_client_create() failed.");
166 context_ptr->error = XML_TRUE;
167 ASSERT(context_ptr->client == NULL);
168 return;
171 if (!ladish_graph_add_client(ladish_studio_get_jack_graph(), context_ptr->client, name, true))
173 log_error("ladish_graph_add_client() failed to add client '%s' to JACK graph", name);
174 context_ptr->error = XML_TRUE;
175 ladish_client_destroy(context_ptr->client);
176 context_ptr->client = NULL;
179 return;
182 if (context_ptr->depth == 2 &&
183 context_ptr->element[0] == PARSE_CONTEXT_PROJECT &&
184 context_ptr->element[1] == PARSE_CONTEXT_CLIENTS)
186 if (!ladish_get_name_and_uuid_attributes("/studio/clients/client", attr, &name, &uuid_str, uuid))
188 context_ptr->error = XML_TRUE;
189 return;
192 log_info("studio client \"%s\" with uuid %s", name, uuid_str);
194 context_ptr->client = ladish_graph_find_client_by_uuid(room_ptr->graph, uuid);
195 if (context_ptr->client != NULL)
197 log_info("Found existing client");
198 return;
201 if (!ladish_client_create(uuid, &context_ptr->client))
203 log_error("ladish_client_create() failed.");
204 context_ptr->error = XML_TRUE;
205 ASSERT(context_ptr->client == NULL);
206 return;
209 if (!ladish_graph_add_client(room_ptr->graph, context_ptr->client, name, true))
211 log_error("ladish_graph_add_client() failed to add client '%s' to room graph", name);
212 context_ptr->error = XML_TRUE;
213 ladish_client_destroy(context_ptr->client);
214 context_ptr->client = NULL;
217 return;
220 log_error("client element in wrong place");
221 ladish_dump_element_stack(context_ptr);
222 context_ptr->error = XML_TRUE;
223 return;
226 if (strcmp(el, "ports") == 0)
228 //log_info("<ports>");
229 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_PORTS;
230 return;
233 if (strcmp(el, "port") == 0)
235 //log_info("<port>");
236 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_PORT;
238 if (context_ptr->port != NULL)
240 log_error("nested ports");
241 context_ptr->error = XML_TRUE;
242 return;
245 if (context_ptr->depth >= 3 &&
246 context_ptr->element[context_ptr->depth - 3] == PARSE_CONTEXT_CLIENTS &&
247 context_ptr->element[context_ptr->depth - 2] == PARSE_CONTEXT_CLIENT &&
248 context_ptr->element[context_ptr->depth - 1] == PARSE_CONTEXT_PORTS)
250 //log_info("client port");
251 if (context_ptr->client == NULL)
253 log_error("client-less port");
254 context_ptr->error = XML_TRUE;
255 return;
258 if (context_ptr->depth == 5 && context_ptr->element[0] == PARSE_CONTEXT_PROJECT && context_ptr->element[1] == PARSE_CONTEXT_JACK)
260 if (!ladish_get_name_and_uuid_attributes("/project/jack/clients/client/ports/port", attr, &name, &uuid_str, uuid))
262 context_ptr->error = XML_TRUE;
263 return;
266 log_info("jack port \"%s\" with uuid %s", name, uuid_str);
268 if (!ladish_port_create(uuid, false, &context_ptr->port))
270 log_error("ladish_port_create() failed.");
271 return;
274 if (!ladish_graph_add_port(ladish_studio_get_jack_graph(), context_ptr->client, context_ptr->port, name, 0, 0, true))
276 log_error("ladish_graph_add_port() failed.");
277 ladish_port_destroy(context_ptr->port);
278 context_ptr->port = NULL;
281 return;
283 else if (context_ptr->depth == 4 && context_ptr->element[0] == PARSE_CONTEXT_PROJECT)
285 if (!ladish_get_name_and_uuid_attributes("/project/clients/client/ports/port", attr, &name, &uuid_str, uuid))
287 context_ptr->error = XML_TRUE;
288 return;
291 context_ptr->port = ladish_graph_find_port_by_uuid(room_ptr->graph, uuid, false);
292 if (context_ptr->port != NULL)
294 if (!ladish_port_is_link(context_ptr->port))
296 log_info("port \"%s\" with uuid %s already exists in room graph and is not a room-studio link", name, uuid_str);
297 context_ptr->error = XML_TRUE;
299 return;
302 context_ptr->port = ladish_graph_find_port_by_uuid(ladish_studio_get_jack_graph(), uuid, false);
303 if (context_ptr->port == NULL)
305 log_info("app port \"%s\" with uuid %s not found in the jack graph", name, uuid_str);
306 context_ptr->error = XML_TRUE;
307 ladish_graph_dump(ladish_studio_get_jack_graph());
308 return;
311 log_info("app port \"%s\" with uuid %s", name, uuid_str);
313 if (!ladish_graph_add_port(room_ptr->graph, context_ptr->client, context_ptr->port, name, 0, 0, true))
315 log_error("ladish_graph_add_port() failed.");
316 ladish_port_destroy(context_ptr->port);
317 context_ptr->port = NULL;
318 return;
321 return;
324 else if (context_ptr->depth == 2 &&
325 context_ptr->element[0] == PARSE_CONTEXT_PROJECT &&
326 context_ptr->element[1] == PARSE_CONTEXT_ROOM)
328 ASSERT(context_ptr->room != NULL);
329 //log_info("room port");
331 if (!ladish_get_name_and_uuid_attributes("/project/room/port", attr, &name, &uuid_str, uuid))
333 context_ptr->error = XML_TRUE;
334 return;
337 log_info("room port \"%s\" with uuid %s", name, uuid_str);
339 if (!ladish_parse_port_type_and_direction_attributes("/project/room/port", attr, &port_type, &port_flags))
341 context_ptr->error = XML_TRUE;
342 return;
345 context_ptr->port = ladish_graph_find_port_by_uuid(room_ptr->graph, uuid, false);
346 if (context_ptr->port == NULL)
348 log_error("Cannot find room link port() failed.");
349 context_ptr->port = NULL;
352 return;
355 log_error("port element in wrong place");
356 ladish_dump_element_stack(context_ptr);
357 context_ptr->error = XML_TRUE;
359 return;
362 if (strcmp(el, "connections") == 0)
364 //log_info("<connections>");
365 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_CONNECTIONS;
366 return;
369 if (strcmp(el, "connection") == 0)
371 //log_info("<connection>");
372 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_CONNECTION;
374 uuid_str = ladish_get_uuid_attribute(attr, "port1", uuid, false);
375 if (uuid_str == NULL)
377 log_error("/studio/connections/connection \"port1\" attribute is not available.");
378 context_ptr->error = XML_TRUE;
379 return;
382 uuid2_str = ladish_get_uuid_attribute(attr, "port2", uuid2, false);
383 if (uuid2_str == NULL)
385 log_error("/studio/connections/connection \"port2\" attribute is not available.");
386 context_ptr->error = XML_TRUE;
387 return;
390 log_info("studio connection between port %s and port %s", uuid_str, uuid2_str);
392 port1 = ladish_graph_find_port_by_uuid(room_ptr->graph, uuid, true);
393 if (port1 == NULL)
395 log_error("studio client with unknown port %s", uuid_str);
396 context_ptr->error = XML_TRUE;
397 return;
400 port2 = ladish_graph_find_port_by_uuid(room_ptr->graph, uuid2, true);
401 if (port2 == NULL)
403 log_error("studio client with unknown port %s", uuid2_str);
404 context_ptr->error = XML_TRUE;
405 return;
408 context_ptr->connection_id = ladish_graph_add_connection(room_ptr->graph, port1, port2, true);
409 if (context_ptr->connection_id == 0)
411 log_error("ladish_graph_add_connection() failed.");
412 return;
415 return;
418 if (strcmp(el, "applications") == 0)
420 //log_info("<applications>");
421 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_APPLICATIONS;
422 return;
425 if (strcmp(el, "application") == 0)
427 //log_info("<application>");
428 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_APPLICATION;
430 name = ladish_get_string_attribute(attr, "name");
431 if (name == NULL)
433 log_error("application \"name\" attribute is not available.");
434 context_ptr->error = XML_TRUE;
435 return;
438 if (ladish_get_bool_attribute(attr, "terminal", &context_ptr->terminal) == NULL)
440 log_error("application \"terminal\" attribute is not available. name=\"%s\"", name);
441 context_ptr->error = XML_TRUE;
442 return;
445 if (ladish_get_bool_attribute(attr, "autorun", &context_ptr->autorun) == NULL)
447 log_error("application \"autorun\" attribute is not available. name=\"%s\"", name);
448 context_ptr->error = XML_TRUE;
449 return;
452 if (ladish_get_byte_attribute(attr, "level", &context_ptr->level) == NULL)
454 log_error("application \"level\" attribute is not available. name=\"%s\"", name);
455 context_ptr->error = XML_TRUE;
456 return;
459 context_ptr->str = strdup(name);
460 if (context_ptr->str == NULL)
462 log_error("strdup() failed");
463 context_ptr->error = XML_TRUE;
464 return;
467 context_ptr->data_used = 0;
468 return;
471 if (strcmp(el, "dict") == 0)
473 //log_info("<dict>");
474 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_DICT;
476 if (context_ptr->dict != NULL)
478 log_error("nested dicts");
479 context_ptr->error = XML_TRUE;
480 return;
483 if (context_ptr->depth == 1 &&
484 context_ptr->element[0] == PARSE_CONTEXT_STUDIO)
486 context_ptr->dict = ladish_graph_get_dict(room_ptr->graph);
487 ASSERT(context_ptr->dict != NULL);
489 else if (context_ptr->depth > 0 &&
490 context_ptr->element[context_ptr->depth - 1] == PARSE_CONTEXT_CLIENT)
492 ASSERT(context_ptr->client != NULL);
493 context_ptr->dict = ladish_client_get_dict(context_ptr->client);
494 ASSERT(context_ptr->dict != NULL);
496 else if (context_ptr->depth > 0 &&
497 context_ptr->element[context_ptr->depth - 1] == PARSE_CONTEXT_PORT)
499 ASSERT(context_ptr->port != NULL);
500 context_ptr->dict = ladish_port_get_dict(context_ptr->port);
501 ASSERT(context_ptr->dict != NULL);
503 else if (context_ptr->depth > 0 &&
504 context_ptr->element[context_ptr->depth - 1] == PARSE_CONTEXT_CONNECTION)
506 ASSERT(context_ptr->port != NULL);
507 context_ptr->dict = ladish_graph_get_connection_dict(room_ptr->graph, context_ptr->connection_id);
508 ASSERT(context_ptr->dict != NULL);
510 else
512 log_error("unexpected dict XML element");
513 context_ptr->error = XML_TRUE;
514 return;
517 return;
520 if (strcmp(el, "key") == 0)
522 //log_info("<key>");
523 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_KEY;
525 if (context_ptr->dict == NULL)
527 log_error("dict-less key");
528 context_ptr->error = XML_TRUE;
529 return;
532 name = ladish_get_string_attribute(attr, "name");
533 if (name == NULL)
535 log_error("dict/key \"name\" attribute is not available.");
536 context_ptr->error = XML_TRUE;
537 return;
540 context_ptr->str = strdup(name);
541 if (context_ptr->str == NULL)
543 log_error("strdup() failed");
544 context_ptr->error = XML_TRUE;
545 return;
548 context_ptr->data_used = 0;
550 return;
553 log_error("unknown element \"%s\"", el);
554 context_ptr->error = XML_TRUE;
557 static void callback_elend(void * data, const char * el)
559 if (context_ptr->error)
561 return;
564 //log_info("element end (depth = %d, element = %u)", context_ptr->depth, context_ptr->element[context_ptr->depth]);
566 if (context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_KEY &&
567 context_ptr->depth > 0 &&
568 context_ptr->element[context_ptr->depth - 1] == PARSE_CONTEXT_DICT)
570 ASSERT(context_ptr->dict != NULL);
571 context_ptr->data[context_ptr->data_used] = 0;
572 log_info("dict key '%s' with value '%s'", context_ptr->str, context_ptr->data);
573 if (!ladish_dict_set(context_ptr->dict, context_ptr->str, context_ptr->data))
575 log_error("ladish_dict_set() failed");
576 context_ptr->error = XML_TRUE;
577 return;
580 else if (context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_ROOM)
582 //log_info("</room>");
584 else if (context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_JACK)
586 //log_info("</jack>");
588 else if (context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_DICT)
590 //log_info("</dict>");
591 ASSERT(context_ptr->dict != NULL);
592 context_ptr->dict = NULL;
594 else if (context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_CLIENT)
596 //log_info("</client>");
597 ASSERT(context_ptr->client != NULL);
598 context_ptr->client = NULL;
600 else if (context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_PORT)
602 //log_info("</port>");
603 ASSERT(context_ptr->port != NULL);
604 context_ptr->port = NULL;
606 else if (context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_APPLICATION)
608 context_ptr->data[unescape(context_ptr->data, context_ptr->data_used, context_ptr->data)] = 0;
609 unescape(context_ptr->str, strlen(context_ptr->str) + 1, context_ptr->str);
611 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);
613 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)
615 log_error("ladish_app_supervisor_add() failed.");
616 context_ptr->error = XML_TRUE;
620 context_ptr->depth--;
622 if (context_ptr->str != NULL)
624 free(context_ptr->str);
625 context_ptr->str = NULL;
628 return;
631 #undef room_ptr
632 #undef context_ptr
634 #define room_ptr ((struct ladish_room *)room_handle)
636 bool ladish_room_load_project(ladish_room_handle room_handle, const char * project_dir)
638 char * path;
639 struct stat st;
640 XML_Parser parser;
641 int bytes_read;
642 void * buffer;
643 int fd;
644 enum XML_Status xmls;
645 struct ladish_parse_context parse_context;
646 bool ret;
648 log_info("Loading project '%s' into room '%s'", project_dir, room_ptr->name);
650 ASSERT(room_ptr->project_dir == NULL);
651 ASSERT(room_ptr->project_name == NULL);
652 ASSERT(!ladish_app_supervisor_has_apps(room_ptr->app_supervisor));
654 ret = false;
656 room_ptr->project_dir = strdup(project_dir);
657 if (room_ptr->project_dir == NULL)
659 log_error("strdup() failed to for project dir");
660 goto exit;
663 path = catdup(project_dir, LADISH_PROJECT_FILENAME);
664 if (path == NULL)
666 log_error("catdup() failed to compose xml file path");
667 goto exit;
670 if (stat(path, &st) != 0)
672 log_error("failed to stat '%s': %d (%s)", path, errno, strerror(errno));
673 goto free_path;
676 fd = open(path, O_RDONLY);
677 if (fd == -1)
679 log_error("failed to open '%s': %d (%s)", path, errno, strerror(errno));
680 goto free_path;
683 parser = XML_ParserCreate(NULL);
684 if (parser == NULL)
686 log_error("XML_ParserCreate() failed to create parser object.");
687 goto close;
690 //log_info("conf file size is %llu bytes", (unsigned long long)st.st_size);
692 /* we are expecting that conf file has small enough size to fit in memory */
694 buffer = XML_GetBuffer(parser, st.st_size);
695 if (buffer == NULL)
697 log_error("XML_GetBuffer() failed.");
698 goto free_parser;
701 bytes_read = read(fd, buffer, st.st_size);
702 if (bytes_read != st.st_size)
704 log_error("read() returned unexpected result.");
705 goto free_parser;
708 //log_info("\n----------\n%s\n-----------\n", buffer);
709 //goto free_parser;
711 parse_context.error = XML_FALSE;
712 parse_context.depth = -1;
713 parse_context.str = NULL;
714 parse_context.client = NULL;
715 parse_context.port = NULL;
716 parse_context.dict = NULL;
717 parse_context.room = room_handle;
719 XML_SetElementHandler(parser, callback_elstart, callback_elend);
720 XML_SetCharacterDataHandler(parser, callback_chrdata);
721 XML_SetUserData(parser, &parse_context);
723 xmls = XML_ParseBuffer(parser, bytes_read, XML_TRUE);
724 if (xmls == XML_STATUS_ERROR && !parse_context.error)
726 log_error("XML_ParseBuffer() failed.");
728 if (xmls == XML_STATUS_ERROR || parse_context.error)
730 goto free_parser;
733 ladish_interlink_clients(room_ptr->graph, room_ptr->app_supervisor);
734 /* ladish_graph_dump(ladish_studio_get_jack_graph()); */
735 /* ladish_graph_dump(room_ptr->graph); */
737 ladish_app_supervisor_set_directory(room_ptr->app_supervisor, project_dir);
738 ladish_graph_trick_dicts(room_ptr->graph);
739 ladish_app_supervisor_autorun(room_ptr->app_supervisor);
741 ladish_recent_project_use(room_ptr->project_dir);
742 ladish_room_emit_project_properties_changed(room_ptr);
744 ret = true;
746 free_parser:
747 XML_ParserFree(parser);
748 close:
749 close(fd);
750 free_path:
751 free(path);
752 exit:
753 if (!ret)
755 ladish_room_unload_project(room_handle);
756 ladish_notify_simple(LADISH_NOTIFY_URGENCY_HIGH, "Project load failed", "Please inspect the ladishd log (~/.ladish/ladish.log) for more info");
759 return ret;
762 #undef room_ptr