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 */
32char __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
62AppManager* gAppManager;
63BufferManager* gBufferManager;
64MediaFilesManager* gMediaFilesManager;
65NodeManager* gNodeManager;
66NotificationManager* gNotificationManager;
67
68
69#define REPLY_TIMEOUT ((bigtime_t)500000)
70
71
72class ServerApp : public BServer {
73public:
74								ServerApp(status_t& error);
75	virtual						~ServerApp();
76
77protected:
78	virtual void				ArgvReceived(int32 argc, char** argv);
79	virtual void				ReadyToRun();
80	virtual bool				QuitRequested();
81	virtual void				MessageReceived(BMessage* message);
82
83private:
84			void				_HandleMessage(int32 code, const void* data,
85									size_t size);
86			void				_LaunchAddOnServer();
87			void				_QuitAddOnServer();
88
89private:
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
100ServerApp::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
123ServerApp::~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
141void
142ServerApp::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
154bool
155ServerApp::QuitRequested()
156{
157	TRACE("ServerApp::QuitRequested()\n");
158	gMediaFilesManager->SaveState();
159	gNodeManager->SaveState();
160
161	_QuitAddOnServer();
162
163	return true;
164}
165
166
167void
168ServerApp::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
190void
191ServerApp::_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
228void
229ServerApp::_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
271void
272ServerApp::_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
905status_t
906ServerApp::_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
922void
923ServerApp::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
974int
975main()
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