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