1//------------------------------------------------------------------------------
2//	Copyright (c) 2001-2005, Haiku, Inc.
3//
4//	Permission is hereby granted, free of charge, to any person obtaining a
5//	copy of this software and associated documentation files (the "Software"),
6//	to deal in the Software without restriction, including without limitation
7//	the rights to use, copy, modify, merge, publish, distribute, sublicense,
8//	and/or sell copies of the Software, and to permit persons to whom the
9//	Software is furnished to do so, subject to the following conditions:
10//
11//	The above copyright notice and this permission notice shall be included in
12//	all copies or substantial portions of the Software.
13//
14//	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15//	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16//	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17//	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18//	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19//	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20//	DEALINGS IN THE SOFTWARE.
21//
22//	File Name:		AppServer.cpp
23//	Author:			DarkWyrm <bpmagic@columbus.rr.com>
24//	Description:	main manager object for the app_server
25//
26//------------------------------------------------------------------------------
27#include "AppServer.h"
28#include "ServerApp.h"
29#include "ServerProtocol.h"
30
31#include <Accelerant.h>
32#include <AppDefs.h>
33#include <Directory.h>
34#include <Entry.h>
35#include <File.h>
36#include <Message.h>
37#include <Path.h>
38#include <PortLink.h>
39#include <StopWatch.h>
40
41#include <stdio.h>
42#include <stdlib.h>
43
44//#define DEBUG_KEYHANDLING
45//#define DEBUG_SERVER
46
47#ifdef DEBUG_KEYHANDLING
48#	define KBTRACE(x) printf x
49#else
50#	define KBTRACE(x) ;
51#endif
52
53#ifdef DEBUG_SERVER
54#	define STRACE(x) printf x
55#else
56#	define STRACE(x) ;
57#endif
58
59// Globals
60
61port_id gAppServerPort;
62
63//! Used to access the app_server from new_decorator
64static AppServer *sAppServer = NULL;
65
66//! Default background color for workspaces
67//RGBColor workspace_default_color(51,102,160);
68
69
70/*!
71	\brief Constructor
72
73	This loads the default fonts, allocates all the major global variables, spawns the main housekeeping
74	threads, loads user preferences for the UI and decorator, and allocates various locks.
75*/
76AppServer::AppServer(void)
77	:
78	fCursorSem(-1),
79	fCursorArea(-1)
80{
81	fMessagePort = create_port(200, SERVER_PORT_NAME);
82	if (fMessagePort == B_NO_MORE_PORTS)
83		debugger("app_server could not create message port");
84
85	gAppServerPort = fMessagePort;
86
87	fAppList = new BList();
88	fQuittingServer = false;
89
90	// We need this in order for new_decorator to be able to instantiate new decorators
91	sAppServer = this;
92
93	// This is necessary to mediate access between the Poller and app_server threads
94	fActiveAppLock = create_sem(1,"app_server_active_sem");
95
96	// This locker is for app_server and Picasso to vy for control of the ServerApp list
97	fAppListLock = create_sem(1,"app_server_applist_sem");
98
99	// Spawn our thread-monitoring thread
100	fPicassoThreadID = spawn_thread(PicassoThread, "picasso", B_NORMAL_PRIORITY, this);
101	if (fPicassoThreadID >= 0)
102		resume_thread(fPicassoThreadID);
103}
104
105/*!
106	\brief Destructor
107
108	Reached only when the server is asked to shut down in Test mode. Kills all apps, shuts down the
109	desktop, kills the housekeeping threads, etc.
110*/
111AppServer::~AppServer()
112{
113}
114
115
116/*!
117	\brief Thread function for watching for dead apps
118	\param data Pointer to the app_server to which the thread belongs
119	\return Throwaway value - always 0
120*/
121int32
122AppServer::PicassoThread(void *data)
123{
124	for (;;) {
125		acquire_sem(sAppServer->fAppListLock);
126		for (int32 i = 0;;) {
127			ServerApp *app = (ServerApp *)sAppServer->fAppList->ItemAt(i++);
128			if (!app)
129				break;
130
131			app->PingTarget();
132		}
133		release_sem(sAppServer->fAppListLock);
134		// we do this every other second so as not to suck *too* many CPU cycles
135		snooze(1000000);
136	}
137	return 0;
138}
139
140
141/*!
142	\brief Starts Input Server
143*/
144void
145AppServer::LaunchInputServer()
146{
147}
148
149
150/*!
151	\brief Starts the Cursor Thread
152*/
153void
154AppServer::LaunchCursorThread()
155{
156}
157
158
159/*!
160	\brief The call that starts it all...
161	\return Always 0
162*/
163thread_id
164AppServer::Run(void)
165{
166	MainLoop();
167	kill_thread(fPicassoThreadID);
168	return 0;
169}
170
171
172//! Main message-monitoring loop for the regular message port - no input messages!
173void
174AppServer::MainLoop(void)
175{
176	BPrivate::PortLink pmsg(-1, fMessagePort);
177
178	while (1) {
179		STRACE(("info: AppServer::MainLoop listening on port %ld.\n", fMessagePort));
180
181		int32 code;
182		status_t err = pmsg.GetNextMessage(code);
183		if (err < B_OK) {
184			STRACE(("MainLoop:pmsg.GetNextMessage() failed\n"));
185			continue;
186		}
187
188		switch (code) {
189			case B_QUIT_REQUESTED:
190			case AS_GET_DESKTOP:
191			case AS_CREATE_APP:
192			case AS_DELETE_APP:
193				DispatchMessage(code, pmsg);
194				break;
195			default:
196			{
197				STRACE(("Server::MainLoop received unexpected code %ld\n",
198					code));
199				break;
200			}
201		}
202	}
203}
204
205/*!
206	\brief Message handling function for all messages sent to the app_server
207	\param code ID of the message sent
208	\param buffer Attachment buffer for the message.
209
210*/
211void
212AppServer::DispatchMessage(int32 code, BPrivate::PortLink &msg)
213{
214	switch (code) {
215		case AS_GET_DESKTOP:
216		{
217			port_id replyPort;
218			if (msg.Read<port_id>(&replyPort) < B_OK)
219				break;
220
221			int32 userID;
222			msg.Read<int32>(&userID);
223
224			BPrivate::LinkSender reply(replyPort);
225			reply.StartMessage(B_OK);
226			reply.Attach<port_id>(fMessagePort);
227			reply.Flush();
228			break;
229		}
230
231		case AS_CREATE_APP:
232		{
233			// Create the ServerApp to node monitor a new BApplication
234
235			// Attached data:
236			// 1) port_id - receiver port of a regular app
237			// 2) port_id - client looper port - for send messages to the client
238			// 2) team_id - app's team ID
239			// 3) int32 - handler token of the regular app
240			// 4) char * - signature of the regular app
241
242			// Find the necessary data
243			team_id	clientTeamID = -1;
244			port_id	clientLooperPort = -1;
245			port_id clientReplyPort = -1;
246			int32 clientToken;
247			char *appSignature = NULL;
248
249			msg.Read<port_id>(&clientReplyPort);
250			msg.Read<port_id>(&clientLooperPort);
251			msg.Read<team_id>(&clientTeamID);
252			msg.Read<int32>(&clientToken);
253			if (msg.ReadString(&appSignature) != B_OK)
254				break;
255
256			port_id serverListen = create_port(DEFAULT_MONITOR_PORT_SIZE, appSignature);
257			if (serverListen < B_OK) {
258				printf("No more ports left. Time to crash. Have a nice day! :)\n");
259				break;
260			}
261
262			// we let the application own the port, so that we get aware when it's gone
263			if (set_port_owner(serverListen, clientTeamID) < B_OK) {
264				delete_port(serverListen);
265				printf("Could not transfer port ownership to client %ld!\n", clientTeamID);
266				break;
267			}
268
269			// Create the ServerApp subthread for this app
270			acquire_sem(fAppListLock);
271
272			ServerApp *app = new ServerApp(clientReplyPort, serverListen, clientLooperPort,
273				clientTeamID, clientToken, appSignature);
274
275			// add the new ServerApp to the known list of ServerApps
276			fAppList->AddItem(app);
277
278			release_sem(fAppListLock);
279
280			BPrivate::PortLink replylink(clientReplyPort);
281			replylink.StartMessage(B_OK);
282			replylink.Attach<int32>(serverListen);
283			replylink.Flush();
284
285			// This is necessary because BPortLink::ReadString allocates memory
286			free(appSignature);
287			break;
288		}
289		case AS_DELETE_APP:
290		{
291			// Delete a ServerApp. Received only from the respective ServerApp when a
292			// BApplication asks it to quit.
293
294			// Attached Data:
295			// 1) thread_id - thread ID of the ServerApp to be deleted
296
297			int32 i = 0, appnum = fAppList->CountItems();
298			ServerApp *srvapp = NULL;
299			thread_id srvapp_id = -1;
300
301			if (msg.Read<thread_id>(&srvapp_id) < B_OK)
302				break;
303
304			acquire_sem(fAppListLock);
305
306			// Run through the list of apps and nuke the proper one
307			for (i = 0; i < appnum; i++) {
308				srvapp = (ServerApp *)fAppList->ItemAt(i);
309
310				if (srvapp != NULL && srvapp->MonitorThreadID() == srvapp_id) {
311					srvapp = (ServerApp *)fAppList->RemoveItem(i);
312					if (srvapp) {
313						delete srvapp;
314						srvapp = NULL;
315					}
316					break;	// jump out of our for() loop
317				}
318			}
319
320			release_sem(fAppListLock);
321			break;
322		}
323
324		case B_QUIT_REQUESTED:
325			break;
326
327		default:
328			// we should never get here.
329			break;
330	}
331}
332
333/*!
334	\brief Send a quick (no attachments) message to all applications
335
336	Quite useful for notification for things like server shutdown, system
337	color changes, etc.
338*/
339void
340AppServer::Broadcast(int32 code)
341{
342	acquire_sem(fAppListLock);
343
344	for (int32 i = 0; i < fAppList->CountItems(); i++) {
345		ServerApp *app = (ServerApp *)fAppList->ItemAt(i);
346
347		if (!app)
348			{ printf("PANIC in AppServer::Broadcast()\n"); continue; }
349		app->PostMessage(code);
350	}
351
352	release_sem(fAppListLock);
353}
354
355/*!
356	\brief Finds the application with the given signature
357	\param sig MIME signature of the application to find
358	\return the corresponding ServerApp or NULL if not found
359
360	This call should be made only when necessary because it locks the app list
361	while it does its searching.
362*/
363ServerApp *
364AppServer::FindApp(const char *sig)
365{
366	if (!sig)
367		return NULL;
368
369	ServerApp *foundapp=NULL;
370
371	acquire_sem(fAppListLock);
372
373	for(int32 i=0; i<fAppList->CountItems();i++)
374	{
375		foundapp=(ServerApp*)fAppList->ItemAt(i);
376		if(foundapp && foundapp->Title() == sig)
377		{
378			release_sem(fAppListLock);
379			return foundapp;
380		}
381	}
382
383	release_sem(fAppListLock);
384
385	// couldn't find a match
386	return NULL;
387}
388
389
390//	#pragma mark -
391
392
393/*!
394	\brief Entry function to run the entire server
395	\param argc Number of command-line arguments present
396	\param argv String array of the command-line arguments
397	\return -1 if the app_server is already running, 0 if everything's OK.
398*/
399int
400main(int argc, char** argv)
401{
402	// There can be only one....
403	if (find_port(SERVER_PORT_NAME) >= B_OK)
404		return -1;
405
406	AppServer app_server;
407	app_server.Run();
408	return 0;
409}
410