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