vfs: check userland buffers before reading them.
[haiku.git] / src / apps / cortex / RouteApp / RouteAppNodeManager.cpp
blob4b4b4d7afc4cff65a63d9afcd8440d459e507229
1 /*
2 * Copyright (c) 1999-2000, Eric Moon.
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions, and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions, and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
27 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 // RouteAppNodeManager.cpp
34 #include "RouteAppNodeManager.h"
36 #include "MediaIcon.h"
37 #include "NodeGroup.h"
38 #include "NodeRef.h"
39 #include "Connection.h"
41 #include "route_app_io.h"
42 #include "ConnectionIO.h"
43 #include "DormantNodeIO.h"
44 #include "LiveNodeIO.h"
45 #include "MediaFormatIO.h"
46 #include "MessageIO.h"
47 #include "NodeSetIOContext.h"
48 #include "StringContent.h"
49 #include "MediaString.h"
51 #include <Autolock.h>
52 #include <Debug.h>
53 #include <Entry.h>
54 #include <Path.h>
56 #include <TimeSource.h>
58 #include <cstring>
59 #include <cstdlib>
60 #include <cstdio>
61 #include <typeinfo>
63 #include "set_tools.h"
65 using namespace std;
67 __USE_CORTEX_NAMESPACE
69 #define D_METHOD(x) //PRINT (x)
70 #define D_HOOK(x) //PRINT (x)
71 #define D_SETTINGS(x) //PRINT (x)
73 // -------------------------------------------------------- //
74 // *** ctor/dtor
75 // -------------------------------------------------------- //
77 RouteAppNodeManager::~RouteAppNodeManager() {
79 _freeIcons();
82 RouteAppNodeManager::RouteAppNodeManager(
83 bool useAddOnHost) :
84 NodeManager(useAddOnHost),
85 m_nextGroupNumber(1) {
87 // pre-cache icons? +++++
91 // -------------------------------------------------------- //
92 // *** group management
93 // -------------------------------------------------------- //
95 // -------------------------------------------------------- //
96 // *** icon management
97 // -------------------------------------------------------- //
99 // fetch cached icon for the given live node; the MediaIcon
100 // instance is guaranteed to last as long as this object.
101 // Returns 0 if the node doesn't exist.
103 const MediaIcon* RouteAppNodeManager::mediaIconFor(
104 media_node_id nodeID,
105 icon_size iconSize) {
107 BAutolock _l(this);
109 uint64 key = _makeIconKey(nodeID, iconSize);
111 icon_map::const_iterator it = m_iconMap.find(key);
112 if(it != m_iconMap.end()) {
113 // already cached
114 return (*it).second;
117 // look up live_node_info
118 NodeRef* ref;
119 status_t err = getNodeRef(nodeID, &ref);
120 if(err < B_OK)
121 return 0;
123 return mediaIconFor(ref->nodeInfo(), iconSize);
126 const MediaIcon* RouteAppNodeManager::mediaIconFor(
127 live_node_info nodeInfo,
128 icon_size iconSize) {
130 uint64 key = _makeIconKey(nodeInfo.node.node, iconSize);
132 icon_map::const_iterator it = m_iconMap.find(key);
133 if(it != m_iconMap.end()) {
134 // already cached
135 return (*it).second;
138 // create & cache icon
139 MediaIcon* icon = new MediaIcon(
140 nodeInfo, iconSize);
142 m_iconMap.insert(
143 icon_map::value_type(key, icon));
145 return icon;
148 // -------------------------------------------------------- //
149 // *** error handling
150 // -------------------------------------------------------- //
152 status_t RouteAppNodeManager::setLogTarget(
153 const BMessenger& target) {
155 BAutolock _l(this);
157 if(!target.IsValid())
158 return B_BAD_VALUE;
160 m_logTarget = target;
161 return B_OK;
164 // -------------------------------------------------------- //
165 // NodeManager hook implementations
166 // -------------------------------------------------------- //
168 void RouteAppNodeManager::nodeCreated(
169 NodeRef* ref) {
171 // prepare the log message
172 BMessage logMsg(M_LOG);
173 BString title = "Node '";
174 title << ref->name() << "' created";
175 logMsg.AddString("title", title);
177 // create a default group for the node
178 // [em 8feb00]
179 NodeGroup* g = createGroup(ref->name());
181 if(ref->kind() & B_TIME_SOURCE) {
182 // notify observers
183 BMessage m(M_TIME_SOURCE_CREATED);
184 m.AddInt32("nodeID", ref->id());
185 notify(&m);
188 // adopt node's time source if it's not the system clock (the default)
189 // [em 20mar00]
190 media_node systemClock;
191 status_t err = roster->GetSystemTimeSource(&systemClock);
192 if(err == B_OK)
194 BTimeSource* ts = roster->MakeTimeSourceFor(ref->node());
195 if (ts == NULL)
196 return;
197 if(ts->Node() != systemClock)
199 g->setTimeSource(ts->Node());
200 logMsg.AddString("line", "Synced to system clock");
202 ts->Release();
205 g->addNode(ref);
207 m_logTarget.SendMessage(&logMsg);
210 void RouteAppNodeManager::nodeDeleted(
211 const NodeRef* ref) {
213 // prepare the log message
214 BMessage logMsg(M_LOG);
215 BString title = "Node '";
216 title << ref->name() << "' released";
217 logMsg.AddString("title", title);
219 if(ref->kind() & B_TIME_SOURCE) {
220 // notify observers
221 BMessage m(M_TIME_SOURCE_DELETED);
222 m.AddInt32("nodeID", ref->id());
223 notify(&m);
226 m_logTarget.SendMessage(&logMsg);
229 void RouteAppNodeManager::connectionMade(
230 Connection* connection) {
232 D_HOOK((
233 "@ RouteAppNodeManager::connectionMade()\n"));
235 status_t err;
237 // prepare the log message
238 BMessage logMsg(M_LOG);
239 BString title = "Connection ";
240 if (strcmp(connection->outputName(), connection->inputName()) == 0) {
241 title << "'" << connection->outputName() << "' ";
243 title << "made";
244 logMsg.AddString("title", title);
246 if(!(connection->flags() & Connection::INTERNAL))
247 // don't react to connection Cortex didn't make
248 return;
250 // create or merge groups
251 NodeRef *producer, *consumer;
252 err = getNodeRef(connection->sourceNode(), &producer);
253 if(err < B_OK) {
254 D_HOOK((
255 "!!! RouteAppNodeManager::connectionMade():\n"
256 " sourceNode (%ld) not found\n",
257 connection->sourceNode()));
258 return;
260 err = getNodeRef(connection->destinationNode(), &consumer);
261 if(err < B_OK) {
262 D_HOOK((
263 "!!! RouteAppNodeManager::connectionMade():\n"
264 " destinationNode (%ld) not found\n",
265 connection->destinationNode()));
266 return;
269 // add node names to log messages
270 BString line = "Between:";
271 logMsg.AddString("line", line);
272 line = " ";
273 line << producer->name() << " and ";
274 line << consumer->name();
275 logMsg.AddString("line", line);
277 // add format to log message
278 line = "Negotiated format:";
279 logMsg.AddString("line", line);
280 line = " ";
281 line << MediaString::getStringFor(connection->format(), false);
282 logMsg.AddString("line", line);
284 NodeGroup *group = 0;
285 BString groupName = "Untitled group ";
286 if(_canGroup(producer) && _canGroup(consumer))
288 if (producer->group() && consumer->group() &&
289 !(producer->group()->groupFlags() & NodeGroup::GROUP_LOCKED) &&
290 !(consumer->group()->groupFlags() & NodeGroup::GROUP_LOCKED))
292 // merge into consumers group
293 group = consumer->group();
294 mergeGroups(producer->group(), group);
296 else if (producer->group() &&
297 !(producer->group()->groupFlags() & NodeGroup::GROUP_LOCKED))
298 { // add consumer to producers group
299 group = producer->group();
300 group->addNode(consumer);
302 else if (consumer->group() &&
303 !(consumer->group()->groupFlags() & NodeGroup::GROUP_LOCKED))
304 { // add producer to consumers group
305 group = consumer->group();
306 group->addNode(producer);
308 else
309 { // make new group for both
310 groupName << m_nextGroupNumber++;
311 group = createGroup(groupName.String());
312 group->addNode(producer);
313 group->addNode(consumer);
316 else if(_canGroup(producer) && !producer->group())
317 { // make new group for producer
318 groupName << m_nextGroupNumber++;
319 group = createGroup(groupName.String());
320 group->addNode(producer);
322 else if(_canGroup(consumer) && !consumer->group())
323 { // make new group for consumer
324 groupName << m_nextGroupNumber++;
325 group = createGroup(groupName.String());
326 group->addNode(consumer);
329 m_logTarget.SendMessage(&logMsg);
332 void RouteAppNodeManager::connectionBroken(
333 const Connection* connection) {
335 D_HOOK((
336 "@ RouteAppNodeManager::connectionBroken()\n"));
338 // prepare the log message
339 BMessage logMsg(M_LOG);
340 BString title = "Connection ";
341 if (strcmp(connection->outputName(), connection->inputName()) == 0) {
342 title << "'" << connection->outputName() << "' ";
344 title << "broken";
345 logMsg.AddString("title", title);
347 if(!(connection->flags() & Connection::INTERNAL))
348 // don't react to connection Cortex didn't make
349 return;
351 status_t err;
353 // if the source and destination nodes belong to the same group,
354 // and if no direct or indirect connection remains between the
355 // source and destination nodes, split groups
357 NodeRef *producer, *consumer;
358 err = getNodeRef(connection->sourceNode(), &producer);
359 if(err < B_OK) {
360 D_HOOK((
361 "!!! RouteAppNodeManager::connectionMade():\n"
362 " sourceNode (%ld) not found\n",
363 connection->sourceNode()));
364 return;
366 err = getNodeRef(connection->destinationNode(), &consumer);
367 if(err < B_OK) {
368 D_HOOK((
369 "!!! RouteAppNodeManager::connectionMade():\n"
370 " destinationNode (%ld) not found\n",
371 connection->destinationNode()));
372 return;
375 // add node names to log messages
376 BString line = "Between:";
377 logMsg.AddString("line", line);
378 line = " ";
379 line << producer->name() << " and ";
380 line << consumer->name();
381 logMsg.AddString("line", line);
384 producer->group() &&
385 producer->group() == consumer->group() &&
386 !findRoute(producer->id(), consumer->id())) {
388 NodeGroup *newGroup;
389 splitGroup(producer, consumer, &newGroup);
392 m_logTarget.SendMessage(&logMsg);
395 void RouteAppNodeManager::connectionFailed(
396 const media_output & output,
397 const media_input & input,
398 const media_format & format,
399 status_t error) {
400 D_HOOK((
401 "@ RouteAppNodeManager::connectionFailed()\n"));
403 status_t err;
405 // prepare the log message
406 BMessage logMsg(M_LOG);
407 BString title = "Connection failed";
408 logMsg.AddString("title", title);
409 logMsg.AddInt32("error", error);
411 NodeRef *producer, *consumer;
412 err = getNodeRef(output.node.node, &producer);
413 if(err < B_OK) {
414 return;
416 err = getNodeRef(input.node.node, &consumer);
417 if(err < B_OK) {
418 return;
421 // add node names to log messages
422 BString line = "Between:";
423 logMsg.AddString("line", line);
424 line = " ";
425 line << producer->name() << " and " << consumer->name();
426 logMsg.AddString("line", line);
428 // add format to log message
429 line = "Tried format:";
430 logMsg.AddString("line", line);
431 line = " ";
432 line << MediaString::getStringFor(format, true);
433 logMsg.AddString("line", line);
435 // and send it
436 m_logTarget.SendMessage(&logMsg);
439 // -------------------------------------------------------- //
440 // *** IPersistent
441 // -------------------------------------------------------- //
443 void RouteAppNodeManager::xmlExportBegin(
444 ExportContext& context) const {
446 status_t err;
448 try {
449 NodeSetIOContext& set = dynamic_cast<NodeSetIOContext&>(context);
450 context.beginElement(_NODE_SET_ELEMENT);
452 // validate the node set
453 for(int n = set.countNodes()-1; n >= 0; --n) {
454 media_node_id id = set.nodeAt(n);
455 ASSERT(id != media_node::null.node);
457 // fetch node
458 NodeRef* ref;
459 err = getNodeRef(id, &ref);
460 if(err < B_OK) {
461 D_SETTINGS((
462 "! RVNM::xmlExportBegin(): node %ld doesn't exist\n", id));
464 set.removeNodeAt(n);
465 continue;
467 // skip unless internal
468 if(!ref->isInternal()) {
469 D_SETTINGS((
470 "! RVNM::xmlExportBegin(): node %ld not internal; skipping.\n", id));
472 set.removeNodeAt(n);
473 continue;
477 catch(bad_cast& e) {
478 context.reportError("RouteAppNodeManager: expected a NodeSetIOContext\n");
482 void RouteAppNodeManager::xmlExportAttributes(
483 ExportContext& context) const {}
485 void RouteAppNodeManager::xmlExportContent(
486 ExportContext& context) const {
488 status_t err;
490 try {
491 NodeSetIOContext& nodeSet = dynamic_cast<NodeSetIOContext&>(context);
492 context.beginContent();
494 // write nodes; enumerate connections
495 typedef map<uint32,Connection> connection_map;
496 connection_map connections;
497 int count = nodeSet.countNodes();
498 for(int n = 0; n < count; ++n) {
499 media_node_id id = nodeSet.nodeAt(n);
500 ASSERT(id != media_node::null.node);
502 // fetch node
503 NodeRef* ref;
504 err = getNodeRef(id, &ref);
505 if(err < B_OK) {
506 D_SETTINGS((
507 "! RouteAppNodeManager::xmlExportContent():\n"
508 " getNodeRef(%ld) failed: '%s'\n",
509 id, strerror(err)));
510 continue;
513 // fetch connections
514 vector<Connection> conSet;
515 ref->getInputConnections(conSet);
516 ref->getOutputConnections(conSet);
517 for(uint32 c = 0; c < conSet.size(); ++c)
518 // non-unique connections will be rejected:
519 connections.insert(
520 connection_map::value_type(conSet[c].id(), conSet[c]));
522 // create an IO object for the node & write it
523 DormantNodeIO io(ref, nodeSet.keyAt(n));
524 if(context.writeObject(&io) < B_OK)
525 // abort
526 return;
529 // write connections
530 for(connection_map::const_iterator it = connections.begin();
531 it != connections.end(); ++it) {
533 ConnectionIO io(
534 &(*it).second,
535 this,
536 &nodeSet);
537 if(context.writeObject(&io) < B_OK)
538 // abort
539 return;
543 // +++++ write groups
545 // write UI state
547 BMessage m;
548 nodeSet.exportUIState(&m);
549 context.beginElement(_UI_STATE_ELEMENT);
550 context.beginContent();
551 MessageIO io(&m);
552 context.writeObject(&io);
553 context.endElement();
556 catch(bad_cast& e) {
557 context.reportError("RouteAppNodeManager: expected a NodeSetIOContext\n");
561 void RouteAppNodeManager::xmlExportEnd(
562 ExportContext& context) const {
564 context.endElement();
567 // -------------------------------------------------------- //
568 // IMPORT
569 // -------------------------------------------------------- //
571 void RouteAppNodeManager::xmlImportBegin(
572 ImportContext& context) {
576 void RouteAppNodeManager::xmlImportAttribute(
577 const char* key,
578 const char* value,
579 ImportContext& context) {}
581 void RouteAppNodeManager::xmlImportContent(
582 const char* data,
583 uint32 length,
584 ImportContext& context) {}
586 void RouteAppNodeManager::xmlImportChild(
587 IPersistent* child,
588 ImportContext& context) {
590 status_t err;
592 if(!strcmp(context.element(), _DORMANT_NODE_ELEMENT)) {
593 DormantNodeIO* io = dynamic_cast<DormantNodeIO*>(child);
594 ASSERT(io);
596 NodeRef* newRef;
597 err = io->instantiate(this, &newRef);
598 if(err == B_OK) {
599 // created node; add an entry to the set stored in the
600 // ImportContext for later reference
601 try {
602 NodeSetIOContext& set = dynamic_cast<NodeSetIOContext&>(context);
603 set.addNode(newRef->id(), io->nodeKey());
605 catch(bad_cast& e) {
606 context.reportError("RouteAppNodeManager: expected a NodeSetIOContext\n");
609 else {
610 D_SETTINGS((
611 "!!! RouteAppNodeManager::xmlImportChild():\n"
612 " DormantNodeIO::instantiate() failed:\n"
613 " '%s'\n",
614 strerror(err)));
617 else if(!strcmp(context.element(), _CONNECTION_ELEMENT)) {
618 ConnectionIO* io = dynamic_cast<ConnectionIO*>(child);
619 ASSERT(io);
621 // instantiate the connection
622 Connection con;
623 err = io->instantiate(
624 this,
625 dynamic_cast<NodeSetIOContext*>(&context),
626 &con);
627 if(err < B_OK) {
628 D_SETTINGS((
629 "!!! ConnectionIO::instantiate() failed:\n"
630 " '%s'\n", strerror(err)));
633 // +++++ group magic?
636 else if(!strcmp(context.element(), _NODE_GROUP_ELEMENT)) {
637 // +++++
639 else if(
640 context.parentElement() &&
641 !strcmp(context.parentElement(), _UI_STATE_ELEMENT)) {
643 // expect a nested message
644 MessageIO* io = dynamic_cast<MessageIO*>(child);
645 if(!io) {
646 BString err;
647 err <<
648 "RouteAppNodeManager: unexpected child '" <<
649 context.element() << "'\n";
650 context.reportError(err.String());
653 // hand it off via the extended context object
654 try {
655 NodeSetIOContext& set = dynamic_cast<NodeSetIOContext&>(context);
656 set.importUIState(io->message());
658 catch(bad_cast& e) {
659 context.reportError("RouteAppNodeManager: expected a NodeSetIOContext\n");
665 void RouteAppNodeManager::xmlImportComplete(
666 ImportContext& context) {
670 // -------------------------------------------------------- //
672 /*static*/
673 void RouteAppNodeManager::AddTo(
674 XML::DocumentType* docType) {
676 // set up the document type
678 MessageIO::AddTo(docType);
679 MediaFormatIO::AddTo(docType);
680 ConnectionIO::AddTo(docType);
681 DormantNodeIO::AddTo(docType);
682 LiveNodeIO::AddTo(docType);
683 _add_string_elements(docType);
686 // -------------------------------------------------------- //
687 // implementation
688 // -------------------------------------------------------- //
690 uint64 RouteAppNodeManager::_makeIconKey(
691 media_node_id nodeID, icon_size iconSize) {
693 return ((uint64)nodeID) << 32 | iconSize;
696 void RouteAppNodeManager::_readIconKey(
697 uint64 key, media_node_id& nodeID, icon_size& iconSize) {
699 nodeID = key >> 32;
700 iconSize = icon_size(key & 0xffffffff);
703 void RouteAppNodeManager::_freeIcons() {
705 ptr_map_delete(
706 m_iconMap.begin(),
707 m_iconMap.end());
710 bool RouteAppNodeManager::_canGroup(NodeRef* ref) const {
712 // sanity check & easy cases
713 ASSERT(ref);
714 if(ref->isInternal())
715 return true;
717 // bar 'touchy' system nodes
718 if(ref == audioMixerNode() || ref == audioOutputNode())
719 return false;
721 return true;
724 // END -- RouteAppNodeManager.cpp --