xref: /haiku/src/servers/media/media_server.cpp (revision 6d2f2ec177bf615a117a7428d71be4330545b320)
1 /*
2  * Copyright (c) 2002, 2003 Marcus Overhagen <Marcus@Overhagen.de>
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files or portions
6  * thereof (the "Software"), to deal in the Software without restriction,
7  * including without limitation the rights to use, copy, modify, merge,
8  * publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so, subject
10  * to the following conditions:
11  *
12  *  * Redistributions of source code must retain the above copyright notice,
13  *    this list of conditions and the following disclaimer.
14  *
15  *  * Redistributions in binary form must reproduce the above copyright notice
16  *    in the  binary, as well as this list of conditions and the following
17  *    disclaimer in the documentation and/or other materials provided with
18  *    the distribution.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26  * THE SOFTWARE.
27  *
28  */
29 
30 
31 /* to comply with the license above, do not remove the following line */
32 char __dont_remove_copyright_from_binary[] = "Copyright (c) 2002, 2003 "
33 	"Marcus Overhagen <Marcus@Overhagen.de>";
34 
35 
36 #include <stdio.h>
37 #include <string.h>
38 
39 #include <Alert.h>
40 #include <Application.h>
41 #include <Autolock.h>
42 #include <Directory.h>
43 #include <Roster.h>
44 #include <MediaDefs.h>
45 #include <MediaFormats.h>
46 #include <MediaRoster.h>
47 #include <Messenger.h>
48 
49 #include <syscalls.h>
50 
51 #include "AppManager.h"
52 #include "BufferManager.h"
53 #include "DataExchange.h"
54 #include "MediaMisc.h"
55 #include "MediaFilesManager.h"
56 #include "NodeManager.h"
57 #include "NotificationManager.h"
58 #include "ServerInterface.h"
59 #include "debug.h"
60 #include "media_server.h"
61 
62 
63 AppManager* gAppManager;
64 BufferManager* gBufferManager;
65 MediaFilesManager* gMediaFilesManager;
66 NodeManager* gNodeManager;
67 NotificationManager* gNotificationManager;
68 
69 
70 #define REPLY_TIMEOUT ((bigtime_t)500000)
71 
72 
73 class ServerApp : BApplication {
74 public:
75 	ServerApp();
76 	~ServerApp();
77 
78 protected:
79 	virtual void				ArgvReceived(int32 argc, char** argv);
80 	virtual void				ReadyToRun();
81 	virtual bool				QuitRequested();
82 	virtual void				MessageReceived(BMessage* message);
83 
84 private:
85 			void				_HandleMessage(int32 code, const void* data,
86 									size_t size);
87 			void				_LaunchAddOnServer();
88 			void				_QuitAddOnServer();
89 
90 private:
91 			port_id				_ControlPort() const { return fControlPort; }
92 
93 	static	int32				_ControlThread(void* arg);
94 
95 			BLocker				fLocker;
96 			port_id				fControlPort;
97 			thread_id			fControlThread;
98 };
99 
100 
101 ServerApp::ServerApp()
102  	:
103  	BApplication(B_MEDIA_SERVER_SIGNATURE),
104 	fLocker("media server locker")
105 {
106  	gNotificationManager = new NotificationManager;
107  	gBufferManager = new BufferManager;
108 	gAppManager = new AppManager;
109 	gNodeManager = new NodeManager;
110 	gMediaFilesManager = new MediaFilesManager;
111 
112 	fControlPort = create_port(64, MEDIA_SERVER_PORT_NAME);
113 	fControlThread = spawn_thread(_ControlThread, "media_server control", 105,
114 		this);
115 	resume_thread(fControlThread);
116 
117 	BMediaRoster* roster = BMediaRoster::Roster();
118 	if (roster->StartWatching(BMessenger(this, this),
119 			B_MEDIA_SERVER_QUIT) != B_OK) {
120 		TRACE("ServerApp: can't watch for B_MEDIA_SERVER_QUIT");
121 	}
122 }
123 
124 
125 
126 ServerApp::~ServerApp()
127 {
128 	TRACE("ServerApp::~ServerApp()\n");
129 
130 	delete_port(fControlPort);
131 	wait_for_thread(fControlThread, NULL);
132 
133 	delete gNotificationManager;
134 	delete gBufferManager;
135 	delete gAppManager;
136 	delete gNodeManager;
137 	delete gMediaFilesManager;
138 
139 	BMediaRoster* roster = BMediaRoster::CurrentRoster();
140 	if (roster->StopWatching(BMessenger(this, this),
141 			B_MEDIA_SERVER_QUIT) != B_OK) {
142 		TRACE("ServerApp: can't unwatch for B_MEDIA_SERVER_QUIT");
143 	}
144 }
145 
146 
147 void
148 ServerApp::ReadyToRun()
149 {
150 	gNodeManager->LoadState();
151 
152 	// make sure any previous media_addon_server is gone
153 	_QuitAddOnServer();
154 	// and start a new one
155 	_LaunchAddOnServer();
156 
157 }
158 
159 
160 bool
161 ServerApp::QuitRequested()
162 {
163 	TRACE("ServerApp::QuitRequested()\n");
164 	gMediaFilesManager->SaveState();
165 	gNodeManager->SaveState();
166 
167 	_QuitAddOnServer();
168 
169 	return true;
170 }
171 
172 
173 void
174 ServerApp::ArgvReceived(int32 argc, char **argv)
175 {
176 	for (int arg = 1; arg < argc; arg++) {
177 		if (strstr(argv[arg], "dump") != NULL) {
178 			gAppManager->Dump();
179 			gNodeManager->Dump();
180 			gBufferManager->Dump();
181 			gNotificationManager->Dump();
182 			gMediaFilesManager->Dump();
183 		}
184 		if (strstr(argv[arg], "buffer") != NULL)
185 			gBufferManager->Dump();
186 		if (strstr(argv[arg], "node") != NULL)
187 			gNodeManager->Dump();
188 		if (strstr(argv[arg], "files") != NULL)
189 			gMediaFilesManager->Dump();
190 		if (strstr(argv[arg], "quit") != NULL)
191 			PostMessage(B_QUIT_REQUESTED);
192 	}
193 }
194 
195 
196 void
197 ServerApp::_LaunchAddOnServer()
198 {
199 	// Try to launch media_addon_server by mime signature.
200 	// If it fails (for example on the Live CD, where the executable
201 	// hasn't yet been mimesetted), try from this application's
202 	// directory
203 	status_t err = be_roster->Launch(B_MEDIA_ADDON_SERVER_SIGNATURE);
204 	if (err == B_OK)
205 		return;
206 
207 	app_info info;
208 	BEntry entry;
209 	BDirectory dir;
210 	entry_ref ref;
211 
212 	err = GetAppInfo(&info);
213 	err |= entry.SetTo(&info.ref);
214 	err |= entry.GetParent(&entry);
215 	err |= dir.SetTo(&entry);
216 	err |= entry.SetTo(&dir, "media_addon_server");
217 	err |= entry.GetRef(&ref);
218 
219 	if (err == B_OK)
220 		be_roster->Launch(&ref);
221 	if (err == B_OK)
222 		return;
223 
224 	BAlert* alert = new BAlert("media_server", "Launching media_addon_server "
225 		"failed.\n\nmedia_server will terminate", "OK");
226 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
227 		alert->Go();
228 	fprintf(stderr, "Launching media_addon_server (%s) failed: %s\n",
229 		B_MEDIA_ADDON_SERVER_SIGNATURE, strerror(err));
230 	exit(1);
231 }
232 
233 
234 void
235 ServerApp::_QuitAddOnServer()
236 {
237 	// nothing to do if it's already terminated
238 	if (!be_roster->IsRunning(B_MEDIA_ADDON_SERVER_SIGNATURE))
239 		return;
240 
241 	// send a quit request to the media_addon_server
242 	BMessenger msger(B_MEDIA_ADDON_SERVER_SIGNATURE);
243 	if (!msger.IsValid()) {
244 		ERROR("Trouble terminating media_addon_server. Messenger invalid\n");
245 	} else {
246 		BMessage msg(B_QUIT_REQUESTED);
247 		status_t err = msger.SendMessage(&msg, (BHandler *)NULL, 2000000);
248 			// 2 sec timeout
249 		if (err != B_OK) {
250 			ERROR("Trouble terminating media_addon_server (2): %s\n",
251 				strerror(err));
252 		}
253 	}
254 
255 	// wait 5 seconds for it to terminate
256 	for (int i = 0; i < 50; i++) {
257 		if (!be_roster->IsRunning(B_MEDIA_ADDON_SERVER_SIGNATURE))
258 			return;
259 		snooze(100000); // 100 ms
260 	}
261 
262 	// try to kill it (or many of them), up to 10 seconds
263 	for (int i = 0; i < 50; i++) {
264 		team_id id = be_roster->TeamFor(B_MEDIA_ADDON_SERVER_SIGNATURE);
265 		if (id < 0)
266 			break;
267 		kill_team(id);
268 		snooze(200000); // 200 ms
269 	}
270 
271 	if (be_roster->IsRunning(B_MEDIA_ADDON_SERVER_SIGNATURE)) {
272 		ERROR("Trouble terminating media_addon_server, it's still running\n");
273 	}
274 }
275 
276 
277 void
278 ServerApp::_HandleMessage(int32 code, const void* data, size_t size)
279 {
280 	TRACE("ServerApp::HandleMessage %#" B_PRIx32 " enter\n", code);
281 	switch (code) {
282 		case SERVER_CHANGE_FLAVOR_INSTANCES_COUNT:
283 		{
284 			const server_change_flavor_instances_count_request& request
285 				= *static_cast<
286 					const server_change_flavor_instances_count_request*>(data);
287 			server_change_flavor_instances_count_reply reply;
288 			status_t status = B_BAD_VALUE;
289 
290 			if (request.delta == 1) {
291 				status = gNodeManager->IncrementFlavorInstancesCount(
292 					request.add_on_id, request.flavor_id, request.team);
293 			} else if (request.delta == -1) {
294 				status = gNodeManager->DecrementFlavorInstancesCount(
295 					request.add_on_id, request.flavor_id, request.team);
296 			}
297 			request.SendReply(status, &reply, sizeof(reply));
298 			break;
299 		}
300 
301 		case SERVER_RESCAN_DEFAULTS:
302 		{
303 			gNodeManager->RescanDefaultNodes();
304 			break;
305 		}
306 
307 		case SERVER_REGISTER_APP:
308 		{
309 			const server_register_app_request& request = *static_cast<
310 				const server_register_app_request*>(data);
311 			server_register_app_reply reply;
312 
313 			status_t status = gAppManager->RegisterTeam(request.team,
314 				request.messenger);
315 			request.SendReply(status, &reply, sizeof(reply));
316 			break;
317 		}
318 
319 		case SERVER_UNREGISTER_APP:
320 		{
321 			const server_unregister_app_request& request = *static_cast<
322 				const server_unregister_app_request*>(data);
323 			server_unregister_app_reply reply;
324 
325 			status_t status = gAppManager->UnregisterTeam(request.team);
326 			request.SendReply(status, &reply, sizeof(reply));
327 			break;
328 		}
329 
330 		case SERVER_GET_ADD_ON_REF:
331 		{
332 			const server_get_add_on_ref_request& request = *static_cast<
333 				const server_get_add_on_ref_request*>(data);
334 			server_get_add_on_ref_reply reply;
335 
336 			entry_ref ref;
337 			reply.result = gNodeManager->GetAddOnRef(request.add_on_id, &ref);
338 			reply.ref = ref;
339 
340 			request.SendReply(reply.result, &reply, sizeof(reply));
341 			break;
342 		}
343 
344 		case SERVER_NODE_ID_FOR:
345 		{
346 			const server_node_id_for_request& request
347 				= *static_cast<const server_node_id_for_request*>(data);
348 			server_node_id_for_reply reply;
349 
350 			status_t status = gNodeManager->FindNodeID(request.port,
351 				&reply.node_id);
352 			request.SendReply(status, &reply, sizeof(reply));
353 			break;
354 		}
355 
356 		case SERVER_GET_LIVE_NODE_INFO:
357 		{
358 			const server_get_live_node_info_request& request = *static_cast<
359 				const server_get_live_node_info_request*>(data);
360 			server_get_live_node_info_reply reply;
361 
362 			status_t status = gNodeManager->GetLiveNodeInfo(request.node,
363 				&reply.live_info);
364 			request.SendReply(status, &reply, sizeof(reply));
365 			break;
366 		}
367 
368 		case SERVER_GET_LIVE_NODES:
369 		{
370 			const server_get_live_nodes_request& request
371 				= *static_cast<const server_get_live_nodes_request*>(data);
372 			server_get_live_nodes_reply reply;
373 			LiveNodeList nodes;
374 
375 			status_t status = gNodeManager->GetLiveNodes(nodes,
376 				request.max_count,
377 				request.has_input ? &request.input_format : NULL,
378 				request.has_output ? &request.output_format : NULL,
379 				request.has_name ? request.name : NULL, request.require_kinds);
380 
381 			reply.count = nodes.size();
382 			reply.area = -1;
383 
384 			live_node_info* infos = reply.live_info;
385 			area_id area = -1;
386 
387 			if (reply.count > MAX_LIVE_INFO) {
388 				// We create an area here, and transfer it to the client
389 				size_t size = (reply.count * sizeof(live_node_info)
390 					+ B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
391 
392 				area = create_area("get live nodes", (void**)&infos,
393 					B_ANY_ADDRESS, size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
394 				if (area < 0) {
395 					reply.area = area;
396 					reply.count = 0;
397 				}
398 			}
399 
400 			for (int32 index = 0; index < reply.count; index++)
401 				infos[index] = nodes[index];
402 
403 			if (area >= 0) {
404 				// transfer the area to the target team
405 				reply.area = _kern_transfer_area(area, &reply.address,
406 					B_ANY_ADDRESS, request.team);
407 				if (reply.area < 0) {
408 					delete_area(area);
409 					reply.count = 0;
410 				}
411 			}
412 
413 			status = request.SendReply(status, &reply, sizeof(reply));
414 			if (status != B_OK && reply.area >= 0) {
415 				// if we couldn't send the message, delete the area
416 				delete_area(reply.area);
417 			}
418 			break;
419 		}
420 
421 		case SERVER_GET_NODE_FOR:
422 		{
423 			const server_get_node_for_request& request
424 				= *static_cast<const server_get_node_for_request*>(data);
425 			server_get_node_for_reply reply;
426 
427 			status_t status = gNodeManager->GetCloneForID(request.node_id,
428 				request.team, &reply.clone);
429 			request.SendReply(status, &reply, sizeof(reply));
430 			break;
431 		}
432 
433 		case SERVER_RELEASE_NODE:
434 		{
435 			const server_release_node_request& request
436 				= *static_cast<const server_release_node_request*>(data);
437 			server_release_node_reply reply;
438 
439 			status_t status = gNodeManager->ReleaseNode(request.node,
440 				request.team);
441 			request.SendReply(status, &reply, sizeof(reply));
442 			break;
443 		}
444 
445 		case SERVER_RELEASE_NODE_ALL:
446 		{
447 			const server_release_node_request& request
448 				= *static_cast<const server_release_node_request*>(data);
449 			server_release_node_reply reply;
450 
451 			status_t status = gNodeManager->ReleaseNodeAll(request.node.node);
452 			request.SendReply(status, &reply, sizeof(reply));
453 			break;
454 		}
455 
456 		case SERVER_REGISTER_NODE:
457 		{
458 			const server_register_node_request& request
459 				= *static_cast<const server_register_node_request*>(data);
460 			server_register_node_reply reply;
461 
462 			status_t status = gNodeManager->RegisterNode(request.add_on_id,
463 				request.flavor_id, request.name, request.kinds, request.port,
464 				request.team, request.timesource_id, &reply.node_id);
465 			request.SendReply(status, &reply, sizeof(reply));
466 			break;
467 		}
468 
469 		case SERVER_UNREGISTER_NODE:
470 		{
471 			const server_unregister_node_request& request
472 				= *static_cast<const server_unregister_node_request*>(data);
473 			server_unregister_node_reply reply;
474 
475 			status_t status = gNodeManager->UnregisterNode(request.node_id,
476 				request.team, &reply.add_on_id, &reply.flavor_id);
477 			request.SendReply(status, &reply, sizeof(reply));
478 			break;
479 		}
480 
481 		case SERVER_PUBLISH_INPUTS:
482 		{
483 			const server_publish_inputs_request& request
484 				= *static_cast<const server_publish_inputs_request*>(data);
485 			server_publish_inputs_reply reply;
486 			status_t status;
487 
488 			if (request.count <= MAX_INPUTS) {
489 				status = gNodeManager->PublishInputs(request.node,
490 					request.inputs, request.count);
491 			} else {
492 				media_input* inputs;
493 				area_id clone;
494 				clone = clone_area("media_inputs clone", (void**)&inputs,
495 					B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA, request.area);
496 				if (clone < B_OK) {
497 					ERROR("SERVER_PUBLISH_INPUTS: failed to clone area, "
498 						"error %#" B_PRIx32 "\n", clone);
499 					status = clone;
500 				} else {
501 					status = gNodeManager->PublishInputs(request.node, inputs,
502 						request.count);
503 					delete_area(clone);
504 				}
505 			}
506 			request.SendReply(status, &reply, sizeof(reply));
507 			break;
508 		}
509 
510 		case SERVER_PUBLISH_OUTPUTS:
511 		{
512 			const server_publish_outputs_request& request
513 				= *static_cast<const server_publish_outputs_request*>(data);
514 			server_publish_outputs_reply reply;
515 			status_t status;
516 
517 			if (request.count <= MAX_OUTPUTS) {
518 				status = gNodeManager->PublishOutputs(request.node,
519 					request.outputs, request.count);
520 			} else {
521 				media_output* outputs;
522 				area_id clone;
523 				clone = clone_area("media_outputs clone", (void**)&outputs,
524 					B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA, request.area);
525 				if (clone < B_OK) {
526 					ERROR("SERVER_PUBLISH_OUTPUTS: failed to clone area, "
527 						"error %#" B_PRIx32 "\n", clone);
528 					status = clone;
529 				} else {
530 					status = gNodeManager->PublishOutputs(request.node, outputs,
531 						request.count);
532 					delete_area(clone);
533 				}
534 			}
535 			request.SendReply(status, &reply, sizeof(reply));
536 			break;
537 		}
538 
539 		case SERVER_GET_NODE:
540 		{
541 			const server_get_node_request& request
542 				= *static_cast<const server_get_node_request*>(data);
543 			server_get_node_reply reply;
544 
545 			status_t status = gNodeManager->GetClone(request.type, request.team,
546 				&reply.node, reply.input_name, &reply.input_id);
547 			request.SendReply(status, &reply, sizeof(reply));
548 			break;
549 		}
550 
551 		case SERVER_SET_NODE:
552 		{
553 			const server_set_node_request& request
554 				= *static_cast<const server_set_node_request*>(data);
555 			server_set_node_reply reply;
556 
557 			status_t status = gNodeManager->SetDefaultNode(request.type,
558 				request.use_node ? &request.node : NULL,
559 				request.use_dni ? &request.dni : NULL,
560 				request.use_input ?  &request.input : NULL);
561 			request.SendReply(status, &reply, sizeof(reply));
562 			break;
563 		}
564 
565 		case SERVER_GET_DORMANT_NODE_FOR:
566 		{
567 			const server_get_dormant_node_for_request& request
568 				= *static_cast<const server_get_dormant_node_for_request*>(
569 					data);
570 			server_get_dormant_node_for_reply reply;
571 
572 			status_t status = gNodeManager->GetDormantNodeInfo(request.node,
573 				&reply.node_info);
574 			request.SendReply(status, &reply, sizeof(reply));
575 			break;
576 		}
577 
578 		case SERVER_GET_INSTANCES_FOR:
579 		{
580 			const server_get_instances_for_request& request
581 				= *static_cast<const server_get_instances_for_request*>(data);
582 			server_get_instances_for_reply reply;
583 
584 			status_t status = gNodeManager->GetInstances(request.add_on_id,
585 				request.flavor_id, reply.node_id, &reply.count,
586 				min_c(request.max_count, MAX_NODE_ID));
587 			if (reply.count == MAX_NODE_ID
588 				&& request.max_count > MAX_NODE_ID) {
589 				// TODO: might be fixed by using an area
590 				PRINT(1, "Warning: SERVER_GET_INSTANCES_FOR: returning "
591 					"possibly truncated list of node id's\n");
592 			}
593 			request.SendReply(status, &reply, sizeof(reply));
594 			break;
595 		}
596 
597 		case SERVER_SET_NODE_TIMESOURCE:
598 		{
599 			const server_set_node_timesource_request& request
600 				= *static_cast<const server_set_node_timesource_request*>(data);
601 			server_set_node_timesource_reply reply;
602 			status_t result = gNodeManager->SetNodeTimeSource(request.node_id,
603 				request.timesource_id);
604 			request.SendReply(result, &reply, sizeof(reply));
605 			break;
606 		}
607 
608 		case SERVER_REGISTER_ADD_ON:
609 		{
610 			const server_register_add_on_request& request = *static_cast<
611 				const server_register_add_on_request*>(data);
612 			server_register_add_on_reply reply;
613 
614 			gNodeManager->RegisterAddOn(request.ref, &reply.add_on_id);
615 			request.SendReply(B_OK, &reply, sizeof(reply));
616 			break;
617 		}
618 
619 		case SERVER_UNREGISTER_ADD_ON:
620 		{
621 			const server_unregister_add_on_command& request = *static_cast<
622 				const server_unregister_add_on_command*>(data);
623 			gNodeManager->UnregisterAddOn(request.add_on_id);
624 			break;
625 		}
626 
627 		case SERVER_REGISTER_DORMANT_NODE:
628 		{
629 			const server_register_dormant_node_command& command
630 				= *static_cast<const server_register_dormant_node_command*>(
631 					data);
632 			if (command.purge_id > 0)
633 				gNodeManager->InvalidateDormantFlavorInfo(command.purge_id);
634 
635 			dormant_flavor_info dormantFlavorInfo;
636 			status_t status = dormantFlavorInfo.Unflatten(command.type,
637 				command.flattened_data, command.flattened_size);
638 			if (status == B_OK)
639 				gNodeManager->AddDormantFlavorInfo(dormantFlavorInfo);
640 			break;
641 		}
642 
643 		case SERVER_GET_DORMANT_NODES:
644 		{
645 			const server_get_dormant_nodes_request& request
646 				= *static_cast<const server_get_dormant_nodes_request*>(data);
647 
648 			server_get_dormant_nodes_reply reply;
649 			reply.count = request.max_count;
650 
651 			dormant_node_info* infos
652 				= new(std::nothrow) dormant_node_info[reply.count];
653 			if (infos != NULL) {
654 				reply.result = gNodeManager->GetDormantNodes(infos,
655 					&reply.count,
656 					request.has_input ? &request.input_format : NULL,
657 					request.has_output ? &request.output_format : NULL,
658 					request.has_name ? request.name : NULL,
659 					request.require_kinds, request.deny_kinds);
660 			} else
661 				reply.result = B_NO_MEMORY;
662 
663 			if (reply.result != B_OK)
664 				reply.count = 0;
665 
666 			request.SendReply(reply.result, &reply, sizeof(reply));
667 			if (reply.count > 0) {
668 				write_port(request.reply_port, 0, infos,
669 					reply.count * sizeof(dormant_node_info));
670 			}
671 			delete[] infos;
672 			break;
673 		}
674 
675 		case SERVER_GET_DORMANT_FLAVOR_INFO:
676 		{
677 			const server_get_dormant_flavor_info_request& request
678 				= *static_cast<const server_get_dormant_flavor_info_request*>(
679 					data);
680 			dormant_flavor_info dormantFlavorInfo;
681 
682 			status_t status = gNodeManager->GetDormantFlavorInfoFor(
683 				request.add_on_id, request.flavor_id, &dormantFlavorInfo);
684 			if (status != B_OK) {
685 				server_get_dormant_flavor_info_reply reply;
686 				reply.result = status;
687 				request.SendReply(reply.result, &reply, sizeof(reply));
688 			} else {
689 				size_t replySize
690 					= sizeof(server_get_dormant_flavor_info_reply)
691 						+ dormantFlavorInfo.FlattenedSize();
692 				server_get_dormant_flavor_info_reply* reply
693 					= (server_get_dormant_flavor_info_reply*)malloc(
694 						replySize);
695 				if (reply != NULL) {
696 					reply->type = dormantFlavorInfo.TypeCode();
697 					reply->flattened_size = dormantFlavorInfo.FlattenedSize();
698 					reply->result = dormantFlavorInfo.Flatten(
699 						reply->flattened_data, reply->flattened_size);
700 
701 					request.SendReply(reply->result, reply, replySize);
702 					free(reply);
703 				} else {
704 					server_get_dormant_flavor_info_reply reply;
705 					reply.result = B_NO_MEMORY;
706 					request.SendReply(reply.result, &reply, sizeof(reply));
707 				}
708 			}
709 			break;
710 		}
711 
712 		case SERVER_SET_NODE_CREATOR:
713 		{
714 			const server_set_node_creator_request& request
715 				= *static_cast<const server_set_node_creator_request*>(data);
716 			server_set_node_creator_reply reply;
717 			status_t status = gNodeManager->SetNodeCreator(request.node,
718 				request.creator);
719 			request.SendReply(status, &reply, sizeof(reply));
720 			break;
721 		}
722 
723 		case SERVER_GET_SHARED_BUFFER_AREA:
724 		{
725 			const server_get_shared_buffer_area_request& request
726 				= *static_cast<const server_get_shared_buffer_area_request*>(
727 					data);
728 			server_get_shared_buffer_area_reply reply;
729 
730 			reply.area = gBufferManager->SharedBufferListArea();
731 			request.SendReply(reply.area >= 0 ? B_OK : reply.area, &reply,
732 				sizeof(reply));
733 			break;
734 		}
735 
736 		case SERVER_REGISTER_BUFFER:
737 		{
738 			const server_register_buffer_request& request
739 				= *static_cast<const server_register_buffer_request*>(data);
740 			server_register_buffer_reply reply;
741 			status_t status;
742 
743 			if (request.info.buffer == 0) {
744 				reply.info = request.info;
745 				// size, offset, flags, area is kept
746 				// get a new beuffer id into reply.info.buffer
747 				status = gBufferManager->RegisterBuffer(request.team,
748 					request.info.size, request.info.flags,
749 					request.info.offset, request.info.area,
750 					&reply.info.buffer);
751 			} else {
752 				reply.info = request.info; // buffer id is kept
753 				status = gBufferManager->RegisterBuffer(request.team,
754 					request.info.buffer, &reply.info.size, &reply.info.flags,
755 					&reply.info.offset, &reply.info.area);
756 			}
757 			request.SendReply(status, &reply, sizeof(reply));
758 			break;
759 		}
760 
761 		case SERVER_UNREGISTER_BUFFER:
762 		{
763 			const server_unregister_buffer_command& request = *static_cast<
764 				const server_unregister_buffer_command*>(data);
765 
766 			gBufferManager->UnregisterBuffer(request.team, request.buffer_id);
767 			break;
768 		}
769 
770 		case SERVER_GET_MEDIA_FILE_TYPES:
771 		{
772 			const server_get_media_types_request& request
773 				= *static_cast<const server_get_media_types_request*>(data);
774 
775 			server_get_media_types_reply reply;
776 			area_id area = gMediaFilesManager->GetTypesArea(reply.count);
777 			if (area >= 0) {
778 				// transfer the area to the target team
779 				reply.area = _kern_transfer_area(area, &reply.address,
780 					B_ANY_ADDRESS, request.team);
781 				if (reply.area < 0) {
782 					delete_area(area);
783 					reply.area = B_ERROR;
784 					reply.count = 0;
785 				}
786 			}
787 
788 			status_t status = request.SendReply(
789 				reply.area < 0 ? reply.area : B_OK, &reply, sizeof(reply));
790 			if (status != B_OK) {
791 				// if we couldn't send the message, delete the area
792 				delete_area(reply.area);
793 			}
794 			break;
795 		}
796 
797 		case SERVER_GET_MEDIA_FILE_ITEMS:
798 		{
799 			const server_get_media_items_request& request
800 				= *static_cast<const server_get_media_items_request*>(data);
801 
802 			server_get_media_items_reply reply;
803 			area_id area = gMediaFilesManager->GetItemsArea(request.type,
804 				reply.count);
805 			if (area >= 0) {
806 				// transfer the area to the target team
807 				reply.area = _kern_transfer_area(area, &reply.address,
808 					B_ANY_ADDRESS, request.team);
809 				if (reply.area < 0) {
810 					delete_area(area);
811 					reply.area = B_ERROR;
812 					reply.count = 0;
813 				}
814 			} else
815 				reply.area = area;
816 
817 			status_t status = request.SendReply(
818 				reply.area < 0 ? reply.area : B_OK, &reply, sizeof(reply));
819 			if (status != B_OK) {
820 				// if we couldn't send the message, delete the area
821 				delete_area(reply.area);
822 			}
823 			break;
824 		}
825 
826 		case SERVER_GET_REF_FOR:
827 		{
828 			const server_get_ref_for_request& request
829 				= *static_cast<const server_get_ref_for_request*>(data);
830 			server_get_ref_for_reply reply;
831 			entry_ref* ref;
832 
833 			status_t status = gMediaFilesManager->GetRefFor(request.type,
834 				request.item, &ref);
835 			if (status == B_OK)
836 				reply.ref = *ref;
837 
838 			request.SendReply(status, &reply, sizeof(reply));
839 			break;
840 		}
841 
842 		case SERVER_SET_REF_FOR:
843 		{
844 			const server_set_ref_for_request& request
845 				= *static_cast<const server_set_ref_for_request*>(data);
846 			server_set_ref_for_reply reply;
847 			entry_ref ref = request.ref;
848 
849 			status_t status = gMediaFilesManager->SetRefFor(request.type,
850 				request.item, ref);
851 			request.SendReply(status, &reply, sizeof(reply));
852 			break;
853 		}
854 
855 		case SERVER_INVALIDATE_MEDIA_ITEM:
856 		{
857 			const server_invalidate_item_request& request
858 				= *static_cast<const server_invalidate_item_request*>(data);
859 			server_invalidate_item_reply reply;
860 
861 			status_t status = gMediaFilesManager->InvalidateItem(request.type,
862 				request.item);
863 			request.SendReply(status, &reply, sizeof(reply));
864 			break;
865 		}
866 
867 		case SERVER_REMOVE_MEDIA_ITEM:
868 		{
869 			const server_remove_media_item_request& request
870 				= *static_cast<const server_remove_media_item_request*>(data);
871 			server_remove_media_item_reply reply;
872 
873 			status_t status = gMediaFilesManager->RemoveItem(request.type,
874 				request.item);
875 			request.SendReply(status, &reply, sizeof(reply));
876 			break;
877 		}
878 
879 		case SERVER_GET_ITEM_AUDIO_GAIN:
880 		{
881 			const server_get_item_audio_gain_request& request
882 				= *static_cast<const server_get_item_audio_gain_request*>(data);
883 			server_get_item_audio_gain_reply reply;
884 
885 			status_t status = gMediaFilesManager->GetAudioGainFor(request.type,
886 				request.item, &reply.gain);
887 			request.SendReply(status, &reply, sizeof(reply));
888 			break;
889 		}
890 
891 		case SERVER_SET_ITEM_AUDIO_GAIN:
892 		{
893 			const server_set_item_audio_gain_request& request
894 				= *static_cast<const server_set_item_audio_gain_request*>(data);
895 			server_set_ref_for_reply reply;
896 
897 			status_t status = gMediaFilesManager->SetAudioGainFor(request.type,
898 				request.item, request.gain);
899 			request.SendReply(status, &reply, sizeof(reply));
900 			break;
901 		}
902 
903 		default:
904 			printf("media_server: received unknown message code %#08" B_PRIx32
905 				"\n", code);
906 	}
907 	TRACE("ServerApp::HandleMessage %#" B_PRIx32 " leave\n", code);
908 }
909 
910 
911 status_t
912 ServerApp::_ControlThread(void* _server)
913 {
914 	ServerApp* server = (ServerApp*)_server;
915 
916 	char data[B_MEDIA_MESSAGE_SIZE];
917 	ssize_t size;
918 	int32 code;
919 	while ((size = read_port_etc(server->_ControlPort(), &code, data,
920 			sizeof(data), 0, 0)) > 0) {
921 		server->_HandleMessage(code, data, size);
922 	}
923 
924 	return B_OK;
925 }
926 
927 
928 void
929 ServerApp::MessageReceived(BMessage* msg)
930 {
931 	TRACE("ServerApp::MessageReceived %lx enter\n", msg->what);
932 	switch (msg->what) {
933 		case MEDIA_SERVER_REQUEST_NOTIFICATIONS:
934 		case MEDIA_SERVER_CANCEL_NOTIFICATIONS:
935 		case MEDIA_SERVER_SEND_NOTIFICATIONS:
936 			gNotificationManager->EnqueueMessage(msg);
937 			break;
938 
939 		case MEDIA_FILES_MANAGER_SAVE_TIMER:
940 			gMediaFilesManager->TimerMessage();
941 			break;
942 
943 		case MEDIA_SERVER_ADD_SYSTEM_BEEP_EVENT:
944 			gMediaFilesManager->HandleAddSystemBeepEvent(msg);
945 			break;
946 
947 		case B_MEDIA_SERVER_QUIT:
948 			gNodeManager->CleanupDormantFlavorInfos();
949 			break;
950 
951 		default:
952 			BApplication::MessageReceived(msg);
953 			printf("\nmedia_server: unknown message received:\n");
954 			msg->PrintToStream();
955 			break;
956 	}
957 	TRACE("ServerApp::MessageReceived %lx leave\n", msg->what);
958 }
959 
960 
961 //	#pragma mark -
962 
963 
964 int
965 main()
966 {
967 	new ServerApp;
968 	be_app->Run();
969 	delete be_app;
970 	return 0;
971 }
972