1/*
2 * Copyright 2001-2011, Haiku, Inc.
3 * Distributed under the terms of the MIT license.
4 *
5 * Authors:
6 *		DarkWyrm <bpmagic@columbus.rr.com>
7 *		Axel Dörfler, axeld@pinc-software.de
8 *		Stephan Aßmus <superstippi@gmx.de>
9 * 		Christian Packmann
10 */
11
12
13#include "AppServer.h"
14
15#include "BitmapManager.h"
16#include "Desktop.h"
17#include "FontManager.h"
18#include "InputManager.h"
19#include "ScreenManager.h"
20#include "ServerProtocol.h"
21
22#include <PortLink.h>
23
24#include <syslog.h>
25
26
27//#define DEBUG_SERVER
28#ifdef DEBUG_SERVER
29#	include <stdio.h>
30#	define STRACE(x) printf x
31#else
32#	define STRACE(x) ;
33#endif
34
35
36// Globals
37port_id gAppServerPort;
38static AppServer* sAppServer;
39BTokenSpace gTokenSpace;
40uint32 gAppServerSIMDFlags = 0;
41
42
43/*!	\brief Constructor
44
45	This loads the default fonts, allocates all the major global variables,
46	spawns the main housekeeping threads, loads user preferences for the UI
47	and decorator, and allocates various locks.
48*/
49AppServer::AppServer()
50	:
51	MessageLooper("app_server"),
52	fMessagePort(-1),
53	fDesktops(),
54	fDesktopLock("AppServerDesktopLock")
55{
56	openlog("app_server", 0, LOG_DAEMON);
57
58	fMessagePort = create_port(DEFAULT_MONITOR_PORT_SIZE, SERVER_PORT_NAME);
59	if (fMessagePort < B_OK)
60		debugger("app_server could not create message port");
61
62	fLink.SetReceiverPort(fMessagePort);
63
64	sAppServer = this;
65
66	gInputManager = new InputManager();
67
68	// Create the font server and scan the proper directories.
69	gFontManager = new FontManager;
70	if (gFontManager->InitCheck() != B_OK)
71		debugger("font manager could not be initialized!");
72
73	gFontManager->Run();
74
75	gScreenManager = new ScreenManager();
76	gScreenManager->Run();
77
78	// Create the bitmap allocator. Object declared in BitmapManager.cpp
79	gBitmapManager = new BitmapManager();
80}
81
82
83/*!	\brief Destructor
84	Reached only when the server is asked to shut down in Test mode.
85*/
86AppServer::~AppServer()
87{
88	delete gBitmapManager;
89
90	gScreenManager->Lock();
91	gScreenManager->Quit();
92
93	gFontManager->Lock();
94	gFontManager->Quit();
95
96	closelog();
97}
98
99
100void
101AppServer::RunLooper()
102{
103	rename_thread(find_thread(NULL), "picasso");
104	_message_thread((void*)this);
105}
106
107
108/*!	\brief Creates a desktop object for an authorized user
109*/
110Desktop*
111AppServer::_CreateDesktop(uid_t userID, const char* targetScreen)
112{
113	BAutolock locker(fDesktopLock);
114	Desktop* desktop = NULL;
115	try {
116		desktop = new Desktop(userID, targetScreen);
117
118		status_t status = desktop->Init();
119		if (status == B_OK) {
120			if (!desktop->Run())
121				status = B_ERROR;
122		}
123		if (status == B_OK && !fDesktops.AddItem(desktop))
124			status = B_NO_MEMORY;
125
126		if (status != B_OK) {
127			syslog(LOG_ERR, "Cannot initialize Desktop object: %s\n",
128				strerror(status));
129			delete desktop;
130			return NULL;
131		}
132	} catch (...) {
133		// there is obviously no memory left
134		return NULL;
135	}
136
137	return desktop;
138}
139
140
141/*!	\brief Finds the desktop object that belongs to a certain user
142*/
143Desktop*
144AppServer::_FindDesktop(uid_t userID, const char* targetScreen)
145{
146	BAutolock locker(fDesktopLock);
147
148	for (int32 i = 0; i < fDesktops.CountItems(); i++) {
149		Desktop* desktop = fDesktops.ItemAt(i);
150
151		if (desktop->UserID() == userID
152			&& ((desktop->TargetScreen() == NULL && targetScreen == NULL)
153				|| (desktop->TargetScreen() != NULL && targetScreen != NULL
154					&& strcmp(desktop->TargetScreen(), targetScreen) == 0))) {
155			return desktop;
156		}
157	}
158
159	return NULL;
160}
161
162
163/*!	\brief Message handling function for all messages sent to the app_server
164	\param code ID of the message sent
165	\param buffer Attachment buffer for the message.
166
167*/
168void
169AppServer::_DispatchMessage(int32 code, BPrivate::LinkReceiver& msg)
170{
171	switch (code) {
172		case AS_GET_DESKTOP:
173		{
174			Desktop* desktop = NULL;
175
176			port_id replyPort;
177			msg.Read<port_id>(&replyPort);
178
179			int32 userID;
180			msg.Read<int32>(&userID);
181
182			char* targetScreen = NULL;
183			msg.ReadString(&targetScreen);
184			if (targetScreen != NULL && strlen(targetScreen) == 0) {
185				free(targetScreen);
186				targetScreen = NULL;
187			}
188
189			int32 version;
190			if (msg.Read<int32>(&version) < B_OK
191				|| version != AS_PROTOCOL_VERSION) {
192				syslog(LOG_ERR, "Application for user %" B_PRId32 " with port "
193					"%" B_PRId32 " does not support the current server "
194					"protocol.\n", userID, replyPort);
195			} else {
196				desktop = _FindDesktop(userID, targetScreen);
197				if (desktop == NULL) {
198					// we need to create a new desktop object for this user
199					// TODO: test if the user exists on the system
200					// TODO: maybe have a separate AS_START_DESKTOP_SESSION for
201					// authorizing the user
202					desktop = _CreateDesktop(userID, targetScreen);
203				}
204			}
205
206			free(targetScreen);
207
208			BPrivate::LinkSender reply(replyPort);
209			if (desktop != NULL) {
210				reply.StartMessage(B_OK);
211				reply.Attach<port_id>(desktop->MessagePort());
212			} else
213				reply.StartMessage(B_ERROR);
214
215			reply.Flush();
216			break;
217		}
218
219#if TEST_MODE
220		case B_QUIT_REQUESTED:
221		{
222			// We've been asked to quit, so (for now) broadcast to all
223			// desktops to quit. This situation will occur only when the server
224			// is compiled as a regular Be application.
225
226			fQuitting = true;
227
228			while (fDesktops.CountItems() > 0) {
229				Desktop *desktop = fDesktops.RemoveItemAt(0);
230
231				thread_id thread = desktop->Thread();
232				desktop->PostMessage(B_QUIT_REQUESTED);
233
234				// we just wait for the desktop to kill itself
235				status_t status;
236				wait_for_thread(thread, &status);
237			}
238
239			delete this;
240
241			// we are now clear to exit
242			exit(0);
243			break;
244		}
245#endif
246
247		default:
248			STRACE(("Server::MainLoop received unexpected code %" B_PRId32 " "
249				"(offset %" B_PRId32 ")\n", code, code - SERVER_TRUE));
250			break;
251	}
252}
253
254
255//	#pragma mark -
256
257
258int
259main(int argc, char** argv)
260{
261	// There can be only one....
262	if (find_port(SERVER_PORT_NAME) >= B_OK)
263		return -1;
264
265	srand(real_time_clock_usecs());
266
267	AppServer* server = new AppServer;
268	server->RunLooper();
269
270	return 0;
271}
272